left-icon

MonoGame Succinctly®
by Jim Perry

Previous
Chapter

of
A
A
A

CHAPTER 5

Input

Input


Until voice-controlled games become the standard, game developers will have to give players a way to control their games. Fortunately, MonoGame makes it easy to hook input devices into your game. In this chapter, we’ll build an input system that recognizes the player using these devices that you can reuse in all of your games. We’ll take a look at the three devices that are typically used in PC and console games. Mobile devices have additional functionality in the form of touch that is the main source of input, but we’re only concentrating on the first two platforms. If you want to port your games to mobile platforms, you’ll have to add touch capability into the input system we’ll develop here.

Gamepad

The gamepad is a combination of mouse and keyboard for consoles, since most gamers don’t want nor have these devices in their living room or other rooms in which consoles are normally used. Gamepads are also much smaller and easier to store when not in use. They don’t have quite as much flexibility as a mouse and keyboard due to the limited number of types of input, so you may have to get creative with how you use them in your game. One method (as seen in the RPG genre) is using on-screen UI elements such as dialogs, menus, and toolbars that the player can bring up or toggle to from controlling their character.

Input devices can have two kinds of controls: digital and analog. Digital controls can be in only one of two states: on or off. Keys on a keyboard are examples of digital controls, as are the buttons on the mouse, and the buttons and DPad on the Xbox 360 controller. Analog controls can have a value within a specific range. The sticks and triggers on the Xbox 360 controller and movements of the mouse return values within a range. The Xbox 360 controller sticks return a floating-point value between −1.0 and 1.0, and the triggers return a value between 0.0 and 1.0. For the mouse, mouse cursor values are returned in pixels.

Each input device has specific advantages and disadvantages. The following table shows a brief comparison:

Input Device

Digital Buttons

Analog Controls

Vibration Effects

Supported on Windows

Supported on Xbox

# Allowed on System

Xbox Controller

14

4

Yes

Yes

Yes

4

Keyboard

> 100

0

No

Yes

Yes

1

Mouse

5

3

No

Yes

No

1

Table 3 – Input Device Platform Comparison

Since you can’t assume that a player on a PC is using a specific type of gamepad (or even an actual gamepad), the GamePad class provides two methods called GetCapabilities for getting the controls on the device that is being used. One method takes an int parameter, the other a PlayerIndex parameter.

The methods return a GamePadCapabilities structure with the following properties:

Name

Description

DisplayName

Gets the gamepad display name.

GamePadType

Gets the type of the controller

HasAButton

Gets a value indicating whether the controller has the button A

HasBButton

Gets a value indicating whether the controller has the button B

HasBackButton

Gets a value indicating whether the controller has the button Back

HasBigButton

Gets a value indicating whether the controller has the guide button

HasDPadDownButton

Gets a value indicating whether the controller has the directional pad down button

HasDPadLeftButton

Gets a value indicating whether the controller has the directional pad left button

HasDPadRightButton

Gets a value indicating whether the controller has the directional pad right button

HasDPadUpButton

Gets a value indicating whether the controller has the directional pad up button

HasLeftShoulderButton

Gets a value indicating whether the controller has the left shoulder button

HasLeftStickButton

Gets a value indicating whether the controller has the left stick button

HasLeftTrigger

Gets a value indicating whether the controller has the left trigger button

HasLeftVibrationMotor

Gets a value indicating whether the controller has the left vibration motor

HasLeftXThumbStick

Gets a value indicating whether the controller has X axis for the left stick (thumbstick) button

HasLeftYThumbStick

Gets a value indicating whether the controller has Y axis for the left stick (thumbstick) button

HasRightShoulderButton

Gets a value indicating whether the controller has the right shoulder button

HasRightStickButton

Gets a value indicating whether the controller has the right stick button

HasRightTrigger

Gets a value indicating whether the controller has the right trigger button

HasRightVibrationMotor

Gets a value indicating whether the controller has the right vibration motor

HasRightXThumbStick

Gets a value indicating whether the controller has X axis for the right stick (thumbstick) button

HasRightYThumbStick

Gets a value indicating whether the controller has Y axis for the right stick (thumbstick) button

HasStartButton

Gets a value indicating whether the controller has the button Start

HasVoiceSupport

Gets a value indicating whether the controller has a microphone

HasXButton

Gets a value indicating whether the controller has the button X

HasYButton

Gets a value indicating whether the controller has the button Y

Identifier

Gets the unique identifier of the gamepad.

IsConnected

Gets a value indicating if the controller is connected

Table 4 – GamePadCapabilities Structure Properties

If you’re going to support any type of controller, you’ll want to check the GamePadType property to make sure whatever controls you want to use are supported on the controller the player is using. You’ll also want to check this if your game is in a genre that needs a specific type of controller, such as a music or rhythm game, or an aircraft simulator or flight game where the player could use a flight stick. The possible values for the GamePadType are:

Name

Description

AlternateGuitar

GamePad is an alternate guitar

ArcadeStick

GamePad is an arcade stick

BigButtonPad

GamePad is a big button pad

DancePad

GamePad is a dance pad

DrumKit

GamePad is a drum kit

FlightStick

GamePad is a flight stick

GamePad

GamePad is the XBOX controller

Guitar

GamePad is a guitar

Unknown

Unknown

Wheel

GamePad is a wheel

Table 5 – GamePadType Fields

The BigButtonPad is a specific type of controller Microsoft introduced with the “Scene It?” games that is useful for quiz-type games. It has a large button at the top that acts as a buzzer and four smaller buttons for answering multiple-choice questions.

If your game uses the vibration motors you’ll use the two SetVibration methods to control them. The methods take three parameters, with the first being either a PlayerIndex or int, and the other two being floats.

There are four GetState methods, although you’ll probably only use the two that take either a PlayerIndex or int parameter. The other two take a GamePadDeadZone parameter that is useful for analog stick controllers.

GamePadState

Every frame, the state of the available input devices is updated and made available through various structures. The first we’ll look at is the structure that contains data for a gamepad. The GamePadState structure gives us the state of the following controls and methods for getting some of them:

Properties

Buttons

DPad

IsConnected

ThumbSticks

Triggers

Methods

IsButtonDown

IsButtonUp

Table 6 – GamePadState Structure Properties and Methods

The structure offers a few more properties and methods than this, but these are the ones that will be used most often.

The properties representing the various controls on the gamepad are themselves structures that contains properties for the various options for that property. Buttons contains all basic controls: A/B/X/Y buttons, Back/Start buttons, and Shoulders/Sticks. Each property in that structure is an enum with a value of Pressed or Released.

DPad has Left, Right, Up, and Down properties that are either Pressed or Released.

Triggers and ThumbSticks use a float and Vector2 data type for the Left and Right controls, respectively, since they do not have to be completely pressed or released. These provide detail that the Button properties do not have. If you’re using either in such a way that you only need to know if they’re pressed or not, use the Buttons properties. This could be for something like navigating UI, for example. You don’t need to know how far the control is pressed, just whether or not it is. If it is, you might move to the next UI element that can be selected. If you’re using the thumbsticks to control a character, you will need to know in what direction and possibly how much it’s being moved.

The IsButtonUp and IsButtonDown methods take a Buttons enum parameter and return a Boolean to indicate if the button or buttons (the parameter can be a bitwise OR to check multiple buttons at once) is pressed or released. This is useful if you have a game that uses combinations of buttons to perform actions, and is easier than checking each button individually.

Keyboard

The Keyboard class contains just two methods, which are used for getting the state of the keyboard(s). The first takes no parameter and returns the state of the current keyboard. The second takes a PlayerIndex parameter and returns the state of the keyboard for a specific player. This method is obsolete, however, so should be avoided.

KeyboardState

The KeyboardState structure has three properties, which you probably won’t use a lot, and three methods, which will probably be what you’ll rely on most often.

Properties

Description

CapsLock

Returns the current state of the CapsLock key

Item

Returns the current state of a specific key

NumLock

Returns the current state of the NumLock key

Methods

Description

GetPressedKeys

Returns an array of the keys that are currently pressed

IsKeyDown

Returns a Boolean that indicates if the specified key is pressed

IsKeyUp

Returns a Boolean that indicates if the specified key is not pressed

Table 7 – KeyboardState Structure Properties and Methods

The GetPressedKeys method is useful if you need to check a lot of keys at the same time without calling the IsKey methods multiple times. This is handy for games that use key combinations. If you only want to check whether a key is pressed or not, the IsKey methods are what you’ll want to use.

Mouse

Like the Keyboard class, the Mouse class is mainly used for getting the state of the mouse. There are two other methods, SetCursor and SetPosition, that you might find useful in certain situations, however.

Instead of drawing your own cursor, you can call the SetCursor method to use the Windows cursor and change it to one of the supplied cursors:

Name

Description

Arrow

Gets the default arrow cursor

Crosshair

Gets the crosshair (“+”) cursor

Hand

Gets the hand cursor

Handle

n/a

IBeam

Gets the cursor that appears when the mouse is over text editing regions

No

Gets the cursor that points that something is invalid, usually a cross

SizeAll

Gets the cursor which points in all directions

SizeNESW

Gets the northeast/southwest (“/”) cursor

SizeNS

Gets the vertical north/south (“|”) cursor

SizeNWSE

Gets the northwest/southeast (“\”) cursor

SizeWE

Gets the horizontal (“-“) cursor

Wait

Gets the waiting cursor that appears while the application/system is busy

WaitArrow

Gets the cross between Arrow and Wait cursors

Table 8 – KeyboardState Structure Properties and Methods

You would have to ensure the cursor is visible by setting the the Game.IsMouseVisible member to true.

The Crosshair cursor would be useful as an aiming reticle, the Hand for indicating that something can be picked up, etc.

The SetPosition method takes two int parameters for the x and y position of the mouse cursor. This could be useful if you’re using the Windows cursor and want to lock it into a certain area or point.

MouseState

The MouseState structure contains the following properties:

Name

Description

HorizontalScrollWheelValue

Returns the cumulative horizontal scroll wheel value since the game start

LeftButton

n/a

MiddleButton

n/a

Position

n/a

RightButton

n/a

ScrollWheelValue

Returns cumulative scroll wheel value since the game start

X

n/a

XButton1

n/a

XButton2

n/a

Y

n/a

Table 9 – MouseState Structure Properties

Most of the properties are fairly obvious. I’ve noted the two that could be misleading. The scroll wheel properties are tracked since the start of the game, not since the last frame. If you want frame-based values, you’ll have to track them yourself.

Implementing input

There are quite a few modifications necessary to our game to get a nice input system up and running. First, add a class to the ScreenManager folder called InputState, and add the following code to it:

using Microsoft.Xna.Framework;

using Microsoft.Xna.Framework.Input;

public class InputState

{

    public KeyboardState CurrentKeyboardState;

    public GamePadState CurrentGamePadState;

    public KeyboardState LastKeyboardState;

    public GamePadState LastGamePadState;

    public bool MenuUp

    {

        get

        {

            return IsNewKeyPress(Keys.Up) ||

                    (CurrentGamePadState.DPad.Up == ButtonState.Pressed &&

                    LastGamePadState.DPad.Up == ButtonState.Released) ||

                    (CurrentGamePadState.ThumbSticks.Left.Y > 0 &&

                    LastGamePadState.ThumbSticks.Left.Y <= 0);

        }

    }

    public bool MenuDown

    {

        get

        {

            return IsNewKeyPress(Keys.Down) ||

                    (CurrentGamePadState.DPad.Down == ButtonState.Pressed &&

                    LastGamePadState.DPad.Down == ButtonState.Released) ||

                    (CurrentGamePadState.ThumbSticks.Left.Y < 0 &&

                    LastGamePadState.ThumbSticks.Left.Y >= 0);

        }

    }

    public bool MenuSelect

    {

        get

        {

            return IsNewKeyPress(Keys.Space) ||

                    IsNewKeyPress(Keys.Enter) ||

                    (CurrentGamePadState.Buttons.A == ButtonState.Pressed &&

                    LastGamePadState.Buttons.A == ButtonState.Released) ||

                    (CurrentGamePadState.Buttons.Start == ButtonState.Pressed &&

                    LastGamePadState.Buttons.Start == ButtonState.Released);

        }

    }

    public bool MenuCancel

    {

        get

        {

            return IsNewKeyPress(Keys.Escape) ||

                    (CurrentGamePadState.Buttons.B == ButtonState.Pressed &&

                    LastGamePadState.Buttons.B == ButtonState.Released) ||

                    (CurrentGamePadState.Buttons.Back == ButtonState.Pressed &&

                    LastGamePadState.Buttons.Back == ButtonState.Released);

        }

    }

    public bool PauseGame

    {

        get

        {

            return IsNewKeyPress(Keys.Escape) ||

                    (CurrentGamePadState.Buttons.Back == ButtonState.Pressed &&

                    LastGamePadState.Buttons.Back == ButtonState.Released) ||

                    (CurrentGamePadState.Buttons.Start == ButtonState.Pressed &&

                    LastGamePadState.Buttons.Start == ButtonState.Released);

        }

    }

    public void Update()

    {

        LastKeyboardState = CurrentKeyboardState;

        LastGamePadState = CurrentGamePadState;

        CurrentKeyboardState = Keyboard.GetState();

        CurrentGamePadState = GamePad.GetState(PlayerIndex.One);

    }

    public bool IsNewKeyPress(Keys key)

    {

        return (CurrentKeyboardState.IsKeyDown(key) &&

                LastKeyboardState.IsKeyUp(key));

    }

    public bool IsKeyDown(Keys key)

    {

        return (CurrentKeyboardState.IsKeyDown(key));

    }

    public bool IsNewKeyUp(Keys key)

    {

        return (CurrentKeyboardState.IsKeyUp(key) &&

                LastKeyboardState.IsKeyDown(key));

    }

}

Code Listing 39 – InputState class

The class holds the state of the keyboard and gamepad for the current frame and the previous frame. This is necessary because examining only the current frame would give false results. If the player holds down the control to fire, for example, even for only a fraction of a second, the game would process that as multiple fire actions if the code only examines the current input state. We need to detect the first frame in which the player initiates a fire action, and this can only be done by comparing the previous frame’s data with the current frame.

The Update method copies what it has as the current frame input data to the previous frame’s input data before obtaining the actual current frame input data via the GetState methods for the keyboard and gamepad.

The Menu… and Pause members check all the various controls that can be used to navigate a menu and return the appropriate value, depending on whether or not the control for that action was used.

The Is…members check the current keyboard state or the current and previous keyboard state for the key passed and return the appropriate value if the key was pressed, up, or down that frame.

As every screen will use this class, we’ll add a virtual method to the GameScreen class that each instance will override:

public virtual void HandleInput(InputState input, GameTime gameTime) { }

Code Listing 40 – GameScreen HandleInput Method

This class is only used for the non-gameplay input. We’ll need something a little more robust and flexible for controlling our character in the actual game.

Our input system will use an interface and class to create a system that will map a specific control to a specific action. We’ll have seven different possible actions the player can take:

  • Fire
  • Move
  • Move backward
  • Move forward
  • Move left
  • Move right
  • Rotate

The interface looks fairly simple, but allows a lot of flexibility. Create a file call IInputSystem in the root project folder and add the following code to it:

using System.Collections.Generic;

interface IInputSystem

{

    List<MappedAction> MappedActions { get; set; }

    void SetActionFunction(string name, ActionDelegate function);

    void Enable();

}

Code Listing 41 – IInputSystem Interface

The MappedAction class is what will allow us to associate a control with an action.

The SetActionFunction is basically what its name says—it allows us to add a function for an action that’s associated with a user-friendly name. The implementation of this method will be done in the InputSystem class we’ll add soon that implements this interface.

The Enable method lets us disable the input system until we get into the actual game. We set up the input system when the game starts and allow the user to configure the controls for actions, but we don’t want the system to actually run until the gameplay starts, since we have a different system that controls the menu system.

It’s time to add the MappedAction class that this interface references. Add this class file to the root folder, and fill it with the following code:

class MappedAction

{

    private string _name;

    private Control _control;

    private ActionType _action;

    private ActionDelegate _function;

    public MappedAction(string name, Control control, ActionType action, ActionDelegate function)

    {

        _name = name;

        _control = control;

        _action = action;

        _function = function;

    }

    public string Name

    {

        get { return _name; }

        set { _name = value; }

    }

    public Control ActionControl

    {

        get { return _control; }

        set { _control = value; }

    }

    public ActionType Action

    {

        get { return _action; }

        set { _action = value; }

    }

    public ActionDelegate Function

    {

        get { return (ActionDelegate) _function; }

        set { _function = value; }

    }

}

Code Listing 42 – MappedAction Class

The Name member allows us to show the user actions as correct English. Since enums cannot have spaces between the different words and might not be named exactly as what you would want to show the user, we have a member that can be set to exactly what we want to show the user.

The Control and ActionType members are enums. We’ll define them now. Add a file called InputSystem to the root folder and add the enums for these two members:

public enum ActionType

{

    Move,

    MoveForward,

    MoveBackward,

    MoveLeft,

    MoveRight,

    Rotate,

    Fire

}

public enum Control

{

    AButton,

    BButton,

    XButton,

    YButton,

    StartButton,

    BackButton,

    LeftTrigger,

    RightTrigger,

    LeftShoulder,

    RightShoulder,

    LeftStick,

    RightStick,

    DPadUp,

    DPadDown,

    DPadLeft,

    DPadRight,

    None,

    Back,

    Tab,

    Enter,

    CapsLock,

    Escape,

    Space,

    PageUp,

    PageDown,

    End,

    Home,

    Left,

    Up,

    Right,

    Down,

    Select,

    Print,

    Execute,

    PrintScreen,

    Insert,

    Delete,

    Help,

    D0,

    D1,

    D2,

    D3,

    D4,

    D5,

    D6,

    D7,

    D8,

    D9,

    A,

    B,

    C,

    D,

    E,

    F,

    G,

    H,

    I,

    J,

    K,

    L,

    M,

    N,

    O,

    P,

    Q,

    R,

    S,

    T,

    U,

    V,

    W,

    X,

    Y,

    Z,

    LeftWindows,

    RightWindows,

    Apps,

    Sleep,

    NumPad0,

    NumPad1,

    NumPad2,

    NumPad3,

    NumPad4,

    NumPad5,

    NumPad6,

    NumPad7,

    NumPad8,

    NumPad9,

    Multiply,

    Add,

    Separator,

    Subtract,

    Decimal,

    Divide,

    F1,

    F2,

    F3,

    F4,

    F5,

    F6,

    F7,

    F8,

    F9,

    F10,

    F11,

    F12,

    F13,

    F14,

    F15,

    F16,

    F17,

    F18,

    F19,

    F20,

    F21,

    F22,

    F23,

    F24,

    NumLock,

    Scroll,

    LeftShift,

    RightShift,

    LeftControl,

    RightControl,

    LeftAlt,

    RightAlt,

    BrowserBack,

    BrowserForward,

    BrowserRefresh,

    BrowserStop,

    BrowserSearch,

    BrowserFavorites,

    BrowserHome,

    VolumeMute,

    VolumeDown,

    VolumeUp,

    MediaNextTrack,

    MediaPreviousTrack,

    MediaStop,

    MediaPlayPause,

    LaunchMail,

    SelectMedia,

    LaunchApplication1,

    LaunchApplication2,

    OemSemicolon,

    OemPlus,

    OemComma,

    OemMinus,

    OemPeriod,

    OemQuestion,

    OemTilde,

    OemOpenBrackets,

    OemPipe,

    OemCloseBrackets,

    OemQuotes,

    Oem8,

    OemBackslash,

    ProcessKey,

    Attn,

    Crsel,

    Exsel,

    EraseEof,

    Play,

    Zoom,

    Pa1,

    OemClear,

    LeftMouseButton,

    MiddleMouseButton,

    RightMouseButton,

    MouseButton1,

    MouseButton2

}

Code Listing 43 – Input System Enums

The Control enum has all possible gamepad, keyboard, and mouse input controls. Many of them you wouldn’t ever consider using for controls for an action, but they’re including for future expansion for any other functionality you might want to add.

The ActionDelegate function is defined as follows:

public delegate void ActionDelegate(object value, GameTime gameTime);

Code Listing 44 – ActionDelegate Defintion

If you’re not familiar with delegates, we’re using them here to define the parameters that are passed for events for each type of action the user can have the character perform. These events will be in the InputSystem class that we’ll define now. Add this to the InputSystem file:

class InputSystem : GameComponent, IInputSystem

{

    public event ActionDelegate Move;

    public event ActionDelegate MoveForward;

    public event ActionDelegate MoveBackward;

    public event ActionDelegate MoveLeft;

    public event ActionDelegate MoveRight;

    public event ActionDelegate Stop;

    public event ActionDelegate Rotate;

    public event ActionDelegate Fire;

    private GamePadState _lastGamePadState;

    private KeyboardState _lastKeyboardState;

    private GamePadState _curGamePadState;

    private KeyboardState _curKeyboardState;

    private MouseState _curMouseState;

    private MouseState _lastMouseState;

    private List<MappedAction> _mappedActions;

    public List<MappedAction> MappedActions

    {

        get { return _mappedActions; }

        set { _mappedActions = value; }

    }

    public InputSystem(Game game)

        : base(game)

    {

        _mappedActions = new List<MappedAction>();

    }

    public override void Initialize()

    {

    }

    public void Enable()

    {

        this.Enabled = true;

    }

       

    public void AddAction(string name, Control control, ActionType type, ActionDelegate function)

    {

        MappedAction action = new MappedAction(name, control, type, function);

        _mappedActions.Add(action);

    }

    public void SetActionControl(string name, Control control)

    {

        foreach (MappedAction action in _mappedActions)

        {

            if (action.Name == name)

            {

                action.ActionControl = control;

                break;

            }

        }

    }

    public void SetActionFunction(string name, ActionDelegate function)

    {

        if (name == "Stop")

            this.Stop += function;

        foreach (MappedAction action in _mappedActions)

        {

            if (action.Name == name)

            {

                action.Function = function;

                break;

            }

        }

    }

    public override void Update(GameTime gameTime)

    {

        _curGamePadState = GamePad.GetState(PlayerIndex.One);

        _curKeyboardState = Keyboard.GetState();

        _curMouseState = Mouse.GetState();

        int dir = -1;

        bool moving = false;

        //we only need to look at the controls mapped to actions

            if (Enabled)

            {

                foreach (MappedAction action in _mappedActions)

                {

                    switch (action.ActionControl)

                    {

                        case Control.AButton:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curGamePadState.Buttons.A, gameTime);

                            }

                            else

                            {

                                if (_curGamePadState.Buttons.A == ButtonState.Pressed && _lastGamePadState.Buttons.A == ButtonState.Released)

                                    action.Function(null, gameTime);

                            }

                            break;

                        case Control.BButton:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curGamePadState.Buttons.B, gameTime);

                            }

                            else

                            {

                                if (_curGamePadState.Buttons.B == ButtonState.Pressed && _lastGamePadState.Buttons.B == ButtonState.Released)

                                    action.Function(null, gameTime);

                            }

                            break;

                        case Control.XButton:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curGamePadState.Buttons.X, gameTime);

                            }

                            else

                            {

                                if (_curGamePadState.Buttons.X == ButtonState.Pressed && _lastGamePadState.Buttons.X == ButtonState.Released)

                                    action.Function(null, gameTime);

                            }

                            break;

                        case Control.YButton:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curGamePadState.Buttons.Y, gameTime);

                            }

                            else

                            {

                                if (_curGamePadState.Buttons.Y == ButtonState.Pressed && _lastGamePadState.Buttons.Y == ButtonState.Released)

                                    action.Function(null, gameTime);

                            }

                            break;

                        case Control.StartButton:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curGamePadState.Buttons.Start, gameTime);

                            }

                            else

                            {

                                if (_curGamePadState.Buttons.Start == ButtonState.Pressed && _lastGamePadState.Buttons.Start == ButtonState.Released)

                                    action.Function(null, gameTime);

                            }

                            break;

                        case Control.BackButton:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curGamePadState.Buttons.Back, gameTime);

                            }

                            else

                            {

                                if (_curGamePadState.Buttons.Back == ButtonState.Pressed && _lastGamePadState.Buttons.Back == ButtonState.Released)

                                    action.Function(null, gameTime);

                            }

                            break;

                        case Control.LeftTrigger:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curGamePadState.Triggers.Left, gameTime);

                            }

                            else

                            {

                                if (_curGamePadState.Triggers.Left == 1.0f && _lastGamePadState.Triggers.Left < 1.0f)

                                    action.Function(null, gameTime);

                            }

                            break;

                   

                        case Control.RightTrigger:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curGamePadState.Triggers.Right, gameTime);

                            }

                            else

                            {

                                if (_curGamePadState.Triggers.Right == 1.0f && _lastGamePadState.Triggers.Right < 1.0f)

                                    action.Function(null, gameTime);

                            }

                            break;

                        case Control.LeftShoulder:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curGamePadState.Buttons.LeftShoulder, gameTime);

                            }

                            else

                            {

                                if (_curGamePadState.Buttons.LeftShoulder == ButtonState.Pressed && _lastGamePadState.Buttons.LeftShoulder == ButtonState.Released)

                                    action.Function(null, gameTime);

                            }

                            break;

                        case Control.RightShoulder:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curGamePadState.Buttons.RightShoulder, gameTime);

                            }

                            else

                            {

                                if (_curGamePadState.Buttons.RightShoulder == ButtonState.Pressed && _lastGamePadState.Buttons.RightShoulder == ButtonState.Released)

                                    action.Function(null, gameTime);

                            }

                            break;

                        case Control.LeftStick:

                            if (action.Action < ActionType.Fire)

                            {

                                //convert to a direction

                                if (_curGamePadState.ThumbSticks.Left != Vector2.Zero)

                                {

                                    float degrees = ((float)(Math.Atan2(_curGamePadState.ThumbSticks.Left.X, _curGamePadState.ThumbSticks.Left.Y) * 57.2957795));

                                    if (degrees < 0)

                                    {

                                        if (degrees >= -67)

                                            dir = (int)Direction.NorthWest;

                                        else if (degrees >= -112)

                                            dir = (int)Direction.West;

                                        else if (degrees >= -157)

                                            dir = (int)Direction.SouthWest;

                                        else

                                            dir = (int)Direction.South;

                                    }

                                    else

                                    {

                                        dir = (int)((int)(degrees / 45));

                                    }

                                    action.Function((Direction)dir, gameTime);

                                    if (action.Action < ActionType.Rotate)

                                        moving = true;

                                }

                                else if (action.Action < ActionType.Rotate)

                                    moving = false;

                            }

                            else

                            {

                                if (_curGamePadState.ThumbSticks.Left != Vector2.Zero && _lastGamePadState.ThumbSticks.Left == Vector2.Zero)

                                    action.Function(_curGamePadState.ThumbSticks.Left, gameTime);

                            }

                            break;

                        case Control.RightStick:

                            if (action.Action < ActionType.Fire)

                            {

                                //convert to a direction

                                if (_curGamePadState.ThumbSticks.Right != Vector2.Zero)

                                {

                                    float degrees = ((float)(Math.Atan2(_curGamePadState.ThumbSticks.Right.X, _curGamePadState.ThumbSticks.Right.Y) * 57.2957795));

                                    if (degrees < 0)

                                    {

                                        if (degrees >= -67)

                                            dir = (int)Direction.NorthWest;

                                        else if (degrees >= -112)

                                            dir = (int)Direction.West;

                                        else if (degrees >= -157)

                                            dir = (int)Direction.SouthWest;

                                        else

                                            dir = (int)Direction.South;

                                    }

                                    else

                                    {

                                        dir = (int)((int)(degrees / 45));

                                    }

                                    action.Function((Direction)dir, gameTime);

                                    if (action.Action < ActionType.Rotate)

                                        moving = true;

                                }

                                else if (action.Action < ActionType.Rotate)

                                    moving = false;

                            }

                            else

                            {

                                if (_curGamePadState.ThumbSticks.Right != Vector2.Zero && _lastGamePadState.ThumbSticks.Right == Vector2.Zero)

                                    action.Function(_curGamePadState.ThumbSticks.Right, gameTime);

                            }

                            break;

                   

                        case Control.DPadUp:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curGamePadState.DPad.Up, gameTime);

                            }

                            else

                            {

                                if (_curGamePadState.DPad.Up == ButtonState.Pressed && _lastGamePadState.DPad.Up == ButtonState.Released)

                                    action.Function(_curGamePadState.DPad.Up, gameTime);

                            }

                            break;

                        case Control.DPadDown:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curGamePadState.DPad.Down, gameTime);

                            }

                            else

                            {

                                if (_curGamePadState.DPad.Down == ButtonState.Pressed && _lastGamePadState.DPad.Down == ButtonState.Released)

                                    action.Function(_curGamePadState.DPad.Down, gameTime);

                            }

                            break;

                        case Control.DPadLeft:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curGamePadState.DPad.Left, gameTime);

                            }

                            else

                            {

                                if (_curGamePadState.DPad.Left == ButtonState.Pressed && _lastGamePadState.DPad.Left == ButtonState.Released)

                                    action.Function(_curGamePadState.DPad.Left, gameTime);

                            }

                            break;

                        case Control.DPadRight:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curGamePadState.DPad.Right, gameTime);

                            }

                            else

                            {

                                if (_curGamePadState.DPad.Right == ButtonState.Pressed && _lastGamePadState.DPad.Right == ButtonState.Released)

                                    action.Function(_curGamePadState.DPad.Right, gameTime);

                            }

                            break;

                        case Control.Back:

                        case Control.Tab:

                        case Control.Enter:

                        case Control.CapsLock:

                        case Control.Escape:

                        case Control.Space:

                        case Control.PageUp:

                        case Control.PageDown:

                        case Control.End:

                        case Control.Home:

                        case Control.Left:

                        case Control.Up:

                        case Control.Right:

                        case Control.Down:

                        case Control.Select:

                        case Control.Print:

                        case Control.Execute:

                        case Control.PrintScreen:

                        case Control.Insert:

                        case Control.Delete:

                        case Control.Help:

                        case Control.D0:

                        case Control.D1:

                        case Control.D2:

                        case Control.D3:

                        case Control.D4:

                        case Control.D5:

                        case Control.D6:

                        case Control.D7:

                        case Control.D8:

                        case Control.D9:

                        case Control.A:

                        case Control.B:

                        case Control.C:

                        case Control.D:

                        case Control.E:

                        case Control.F:

                        case Control.G:

                        case Control.H:

                        case Control.I:

                        case Control.J:

                        case Control.K:

                        case Control.L:

                        case Control.M:

                        case Control.N:

                        case Control.O:

                        case Control.P:

                        case Control.Q:

                        case Control.R:

                        case Control.S:

                        case Control.T:

                        case Control.U:

                        case Control.V:

                        case Control.W:

                        case Control.X:

                        case Control.Y:

                        case Control.Z:

                        case Control.LeftWindows:

                        case Control.RightWindows:

                        case Control.Apps:

                        case Control.Sleep:

                        case Control.NumPad0:

                        case Control.NumPad1:

                        case Control.NumPad2:

                        case Control.NumPad3:

                        case Control.NumPad4:

                        case Control.NumPad5:

                        case Control.NumPad6:

                        case Control.NumPad7:

                        case Control.NumPad8:

                        case Control.NumPad9:

                        case Control.Multiply:

                        case Control.Add:

                        case Control.Separator:

                        case Control.Subtract:

                        case Control.Decimal:

                        case Control.Divide:

                        case Control.F1:

                        case Control.F2:

                        case Control.F3:

                        case Control.F4:

                        case Control.F5:

                        case Control.F6:

                        case Control.F7:

                        case Control.F8:

                        case Control.F9:

                        case Control.F10:

                        case Control.F11:

                        case Control.F12:

                        case Control.F13:

                        case Control.F14:

                        case Control.F15:

                        case Control.F16:

                        case Control.F17:

                        case Control.F18:

                        case Control.F19:

                        case Control.F20:

                        case Control.F21:

                        case Control.F22:

                        case Control.F23:

                        case Control.F24:

                        case Control.NumLock:

                        case Control.Scroll:

                        case Control.LeftShift:

                        case Control.RightShift:

                        case Control.LeftControl:

                        case Control.RightControl:

                        case Control.LeftAlt:

                        case Control.RightAlt:

                        case Control.BrowserBack:

                        case Control.BrowserForward:

                        case Control.BrowserRefresh:

                        case Control.BrowserStop:

                        case Control.BrowserSearch:

                        case Control.BrowserFavorites:

                        case Control.BrowserHome:

                        case Control.VolumeMute:

                        case Control.VolumeDown:

                        case Control.VolumeUp:

                        case Control.MediaNextTrack:

                        case Control.MediaPreviousTrack:

                        case Control.MediaStop:

                        case Control.MediaPlayPause:

                        case Control.LaunchMail:

                        case Control.SelectMedia:

                        case Control.LaunchApplication1:

                        case Control.LaunchApplication2:

                        case Control.OemSemicolon:

                        case Control.OemPlus:

                        case Control.OemComma:

                        case Control.OemMinus:

                        case Control.OemPeriod:

                        case Control.OemQuestion:

                        case Control.OemTilde:

                        case Control.OemOpenBrackets:

                        case Control.OemPipe:

                        case Control.OemCloseBrackets:

                        case Control.OemQuotes:

                        case Control.Oem8:

                        case Control.OemBackslash:

                        case Control.ProcessKey:

                        case Control.Attn:

                        case Control.Crsel:

                        case Control.Exsel:

                        case Control.EraseEof:

                        case Control.Play:

                        case Control.Zoom:

                        case Control.Pa1:

                        case Control.OemClear:

                            //we can use one case here

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curKeyboardState.IsKeyDown((Keys)(action.ActionControl - (action.ActionControl - 1))), gameTime);

                            }

                            else

                            {

                                if (_curKeyboardState.IsKeyDown((Keys)(action.ActionControl - (action.ActionControl - 1))) && _lastKeyboardState.IsKeyUp((Keys)(action.ActionControl - (action.ActionControl - 1))))

                                    action.Function(null, gameTime);

                            }

                            break;

                       

                        case Control.LeftMouseButton:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curMouseState.LeftButton, gameTime);

                            }

                            else

                            {

                                if (_curMouseState.LeftButton == ButtonState.Pressed && _lastMouseState.LeftButton == ButtonState.Released)

                                    action.Function(null, gameTime);

                            }

                            break;

                        case Control.MiddleMouseButton:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curMouseState.MiddleButton, gameTime);

                            }

                            else

                            {

                                if (_curMouseState.MiddleButton == ButtonState.Pressed && _lastMouseState.MiddleButton == ButtonState.Released)

                                    action.Function(null, gameTime);

                            }

                            break;

                        case Control.RightMouseButton:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curMouseState.RightButton, gameTime);

                            }

                            else

                            {

                                if (_curMouseState.RightButton == ButtonState.Pressed && _lastMouseState.RightButton == ButtonState.Released)

                                    action.Function(null, gameTime);

                            }

                            break;

                        case Control.MouseButton1:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curMouseState.XButton1, gameTime);

                            }

                            else

                            {

                                if (_curMouseState.XButton1 == ButtonState.Pressed && _lastMouseState.XButton1 == ButtonState.Released)

                                    action.Function(null, gameTime);

                            }

                            break;

                        case Control.MouseButton2:

                            if (action.Action < ActionType.Fire)

                            {

                                action.Function(_curMouseState.XButton2, gameTime);

                            }

                            else

                            {

                                if (_curMouseState.XButton2 == ButtonState.Pressed && _lastMouseState.XButton2 == ButtonState.Released)

                                    action.Function(null, gameTime);

                            }

                            break;

                    }

                }

                if (!moving)

                {

                    Stop(null, gameTime);

                }

            }

        _lastGamePadState = _curGamePadState;

        _lastKeyboardState = _curKeyboardState;

        _lastMouseState=_curMouseState;

               

    }

}

Code Listing 45 – InputSystem class

There’s a lot going on here, so let’s take a closer look.

The first thing we see is the implementation of the ActionDelegate delegate. We have the eight types of actions for which we’ll do something. Notice that there’s an ActionDelegate for Stop. If the player isn’t doing one of the actions related to moving that we mentioned before, we stop the character from moving.

The next members are the current and previous states of the input devices we’ll handle.

After this, we have the implementation of our interface. We also provide a variety of methods for allowing the modification of mapped actions.

The Update method is the largest chunk of code in the class, as you would expect. After getting the current state of the input devices, if the input system is enabled, we go through all of our mapped actions, examining the current input to see if the conditions match to fire the function associated with that action.

If, after evaluating all the actions, it’s determined that the character is not moving, the Stop action function is called. We then set the current input states to the last input states.

The InputSystem instance is created in the Game class:

InputSystem input;

protected override void Initialize()

{

. . .

 

    input = new InputSystem(this);

    //we don't want to use this until we play the game or configure the controls

    input.Enabled = false;

    input.AddAction("Move", Control.LeftStick, ActionType.Move, null);

    input.AddAction("Move Foward", Control.DPadUp, ActionType.MoveForward, null);

    input.AddAction("Move Backward", Control.DPadDown, ActionType.MoveBackward, null);

    input.AddAction("Move Left", Control.DPadLeft, ActionType.MoveLeft, null);

    input.AddAction("Move Right", Control.DPadRight, ActionType.MoveRight, null);

    input.AddAction("Fire", Control.RightTrigger, ActionType.Fire, null);

    input.AddAction("Rotate", Control.RightStick, ActionType.Rotate, null);

    Components.Add(input);

    this.Services.AddService(typeof(IInputSystem), input);

}

Code Listing 46 – Input System Activation

The input system is enabled only when the player has started the actual gameplay. This happens in the Initialize method of the GameplayScreen class:

IInputSystem input = ((IInputSystem)ScreenManager.Game.Services.GetService(typeof(IInputSystem)));

input.SetActionFunction("Move", Move);

input.SetActionFunction("Move Foward", MoveForward);

input.SetActionFunction("Move Backward", MoveBackward);

input.SetActionFunction("Move Left", MoveLeft);

input.SetActionFunction("Move Right", MoveRight);

input.SetActionFunction("Fire", Fire);

input.SetActionFunction("Rotate", Rotate);

input.SetActionFunction("Stop", Stop);

input.Enable();

Code Listing 47 – Input System Activation

The ActionDelegate parameter for the SetActionFunction method calls are methods in the GameplayScreen class:

private void Move(object value, GameTime gameTime)

{

    _entityManager.MovePlayer((Direction)value);

}

private void MoveForward(object value, GameTime gameTime)

{

    if (value != null)

    {

        if ((ButtonState)value == ButtonState.Pressed)

        {

            _entityManager.MovePlayer(_entityManager.GetPlayerDirection());

        }

    }

}

private void MoveBackward(object value, GameTime gameTime)

{

    if (value != null)

    {

        if ((ButtonState)value == ButtonState.Pressed)

        {

            Direction dir = _entityManager.GetPlayerDirection();

            if (dir < Direction.South)

                dir += 4;

            else

                dir = (Direction)((int)Direction.NorthWest - (int)dir);

            _entityManager.MovePlayer(dir);

        }

    }

}

private void MoveLeft(object value, GameTime gameTime)

{

    if (value != null)

    {

        if ((ButtonState)value == ButtonState.Pressed)

        {

            Direction dir = _entityManager.GetPlayerDirection();

            if (dir < Direction.East)

                dir += 6;

            else

                dir = (Direction)((int)Direction.NorthWest - (int)dir);

            _entityManager.MovePlayer(dir);

        }

    }

}

private void MoveRight(object value, GameTime gameTime)

{

    if (value != null)

    {

        if ((ButtonState)value == ButtonState.Pressed)

        {

            Direction dir = _entityManager.GetPlayerDirection();

            if (dir < Direction.West)

                dir += 2;

            else

                dir = (Direction)((int)Direction.NorthWest - (int)dir);

            _entityManager.MovePlayer(dir);

        }

    }

}

private void Rotate(object value, GameTime gameTime)

{

    if (value != null)

    {

        _entityManager.SetPlayerDirection((Direction)value);

    }

}

private void Stop(object value, GameTime gameTime)

{

    _entityManager.StopPlayer();

}

private void Fire(object value, GameTime gameTime)

{

    _entityManager.PlayerFire();

}

Code Listing 48 – Input System Action Methods

The EntityManager class takes care of resolving all the actions that the player takes. We have a bit of the EntityManager started already, and we’ll continue fleshing it out with the methods called here:

public void SetPlayerDirection(Direction dir)

{

    if (_entities.Count > 0)

    {

        _entities[0].MoveDirection = dir;

    }

}

public void StopPlayer()

{

    if (_entities.Count > 0)

    {

        _entities[0].Speed = 0.0f;

    }

}

public Direction GetPlayerDirection()

{

    if (_entities.Count > 0)

    {

        return _entities[0].MoveDirection;

    }

    else

        return Direction.North;

}

public void MovePlayer(Direction dir)

{

    if (_entities.Count > 0)

    {

        _entities[0].MoveDirection = dir;

        _entities[0].Speed = 3.0f;

    }

}

public void PlayerFire()

{

    if (_entities.Count > 0)

    {

       

    }

}

Code Listing 49 – EntityManager Action Methods

The methods include a sanity check to ensure we’re not attempting to set properties of a null object. This should never happen, but such validation is good practice.

If we move the character, we ensure his speed is set to the default. You could use a constant here, with other constants that could be used for a character that has had his speed enhanced to be faster or slower than normal. This could be done if you put powerups into the game.

Notice the empty if statement in the PlayerFire method. We’ll implement audio in the next chapter.

The last piece we need to implement is the InputState instance. That goes in the ScreenManager class, where it’s also updated and passed to the screens:

InputState _input = new InputState();

public override void Update(GameTime gameTime)

{

    // Read the keyboard and gamepad.

    _input.Update();

. . .

    while (_screensToUpdate.Count > 0)

    {

        // Pop the topmost screen off the waiting list.

        GameScreen screen = _screensToUpdate[_screensToUpdate.Count - 1];

        _screensToUpdate.RemoveAt(_screensToUpdate.Count - 1);

        // Update the screen.

        screen.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);

        if (screen.ScreenState == ScreenState.TransitionOn ||

            screen.ScreenState == ScreenState.Active)

        {

            // If this is the first active screen we came across,

            // give it a chance to handle input.

            if (!otherScreenHasFocus)

            {

                screen.HandleInput(_input, gameTime);

                otherScreenHasFocus = true;

            }

            // If this is an active non-popup, inform any subsequent

            // screens that they are covered by it.

            if (!screen.IsPopup)

                coveredByOtherScreen = true;

        }

    }

    if (_traceEnabled)

        TraceScreens();

}

Code Listing 50 – ScreenManager Updated Update Method

We need to add input handling to a few classes, starting with the the HighScoreScreen class:

public override void HandleInput(InputState input, GameTime gameTime)

{

    if ((input.CurrentGamePadState.Buttons.B == Microsoft.Xna.Framework.Input.ButtonState.Pressed && input.LastGamePadState.Buttons.B == Microsoft.Xna.Framework.Input.ButtonState.Released) ||

        (input.CurrentKeyboardState.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape) && input.LastKeyboardState.IsKeyUp(Microsoft.Xna.Framework.Input.Keys.Escape)))

        this.ExitScreen();

    base.HandleInput(input, gameTime);

}

Code Listing 51 – HighScoreScreen HandleInput Method

The MessageBoxScreen class:

public override void HandleInput(InputState input, GameTime gameTime)

{

    if (input.MenuSelect)

    {

        // Raise the accepted event, then exit the message box.

        if (Accepted != null)

            Accepted(this, EventArgs.Empty);

           

        ExitScreen();

    }

    else if (input.MenuCancel)

    {

        // Raise the cancelled event, then exit the message box.

        if (Cancelled != null)

            Cancelled(this, EventArgs.Empty);

        ExitScreen();

    }

}

Code Listing 52 – MessageBoxScreen HandleInput Method

The GameplayScreen class:

public override void HandleInput(InputState input, GameTime gameTime)

{

    if (input == null)

        throw new ArgumentNullException("input");

    if (input.PauseGame)

    {

        ScreenManager.AddScreen(new PauseMenuScreen());

    }

}

Code Listing 53 – GameplayScreen HandleInput Method

The MenuScreen class:

public override void HandleInput(InputState input, GameTime gameTime)

{

    // Move to the previous menu entry?

    if (input.MenuUp)

    {

        _selectedEntry--;

        if (_selectedEntry < 0)

            _selectedEntry = _menuEntries.Count - 1;

    }

    // Move to the next menu entry?

    if (input.MenuDown)

    {

        _selectedEntry++;

        if (_selectedEntry >= _menuEntries.Count)

            _selectedEntry = 0;

    }

    // Accept or cancel the menu?

    if (input.MenuSelect)

    {

        OnSelectEntry(_selectedEntry);

    }

    else if (input.MenuCancel)

    {

        OnCancel();

    }

    if (input.IsNewKeyPress(Keys.Left))

        OnNewArrowDown(Keys.Left, _selectedEntry);

    else if (input.IsKeyDown(Keys.Left))

        OnArrowDown(Keys.Left, _selectedEntry, gameTime);

               

    if (input.IsNewKeyUp(Keys.Left))

        OnArrowUp(Keys.Left, _selectedEntry);

    if (input.IsNewKeyPress(Keys.Right))

        OnNewArrowDown(Keys.Right, _selectedEntry);

    else if (input.IsKeyDown(Keys.Right))

        OnArrowDown(Keys.Right,_selectedEntry, gameTime);

    if (input.IsNewKeyUp(Keys.Right))

        OnArrowUp(Keys.Right, _selectedEntry);

}

Code Listing 54 – MenuScreen HandleInput Method

Now that we have an input system, we need to give the player a way to change which controls map to the actions we’ve defined. We’ll add a new screen to allow this. Create a new class in the Screens folder and add the following:

class ControlsScreen : MenuScreen

{

    private string[] _items;

    private int[] _selectedIndices;

    private bool _leftArrowDown = false;

    private bool _rightArrowDown = false;

    private float _leftArrowDownTime = 0.0f;

    private float _rightArrowDownTime = 0.0f;

    private List<MappedAction> _actions;

    public ControlsScreen()

    {

    }

    public override void Initialize()

    {

        base.Initialize();

        //get the current configuration and load the menu

        _actions = ((IInputSystem)ScreenManager.Game.Services.GetService(typeof(IInputSystem))).MappedActions;

        _selectedIndices = new int[_actions.Count];

        int count = 0;

        foreach (MappedAction action in _actions)

        {

            _menuEntries.Add(action.Name + ": ");

            _selectedIndices[count] = (int)action.ActionControl;

            count++;

        }

        _menuEntries.Add("Back");

        //populate items array with all available controls

        _items = new string[System.Enum.GetNames(typeof(Control)).Length];

        count = 0;

        //Loop through Keys enum

        foreach (int value in System.Enum.GetValues(typeof(Control)))

        {

            _items[count] = System.Enum.GetName(typeof(Control), value);

            count++;

        }

    }

    public override void Update(GameTime gameTime, bool otherScreenHasFocus,

                                                    bool coveredByOtherScreen)

    {

        base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);

        int count = 0;

        string entry;

        for (count = 0; count < _actions.Count;count++ )

        {

            entry = _actions[count].Name + ": " + _items[_selectedIndices[count]];

            _menuEntries[count] = entry;

        }

    }

    protected override void OnSelectEntry(int entryIndex)

    {

        //increment and check item

        if (entryIndex == 7)

        {

            // Go back to the main menu.

            ExitScreen();

        }

    }

    protected override void OnArrowUp(Keys arrow, int entryIndex)

    {

        int index = _selectedIndices[entryIndex]++;

        switch (arrow)

        {

            case Keys.Left:

                index--;

                if (index < 0)

                    index = _items.Length - 1;

                break;

            case Keys.Right:

                index++;

                if (index > _items.Length - 1)

                    index = 0;

                break;

        }

        _selectedIndices[entryIndex] = index;

           

    }

    protected override void OnArrowDown(Keys arrow, int entryIndex, GameTime gameTime)

    {

        int index = _selectedIndices[entryIndex]++;

        switch (arrow)

        {

            case Keys.Left:

                if (_leftArrowDown)

                {

                    _leftArrowDownTime += gameTime.ElapsedGameTime.Milliseconds;

                    if (_leftArrowDownTime > 250.0f)

                    {

                        index--;

                        _leftArrowDownTime = 0.0f;

                    }

                }

                else

                {

                    _leftArrowDown = true;

                    _leftArrowDownTime = 0.0f;

                }

                break;

            case Keys.Right:

                if (_rightArrowDown)

                {

                    _rightArrowDownTime += gameTime.ElapsedGameTime.Milliseconds;

                    if (_rightArrowDownTime > 250.0f)

                    {

                        index++;

                        _rightArrowDownTime = 0.0f;

                    }

                }

                else

                {

                    _rightArrowDown = true;

                    _rightArrowDownTime = 0.0f;

                }

                break;

        }

        if (index < 0)

            index = _items.Length - 1;

        else if (index == _items.Length)

            index=0;

        _selectedIndices[entryIndex] = index;

    }

    protected override void OnCancel()

    {

        ExitScreen();

    }

    public override void ExitScreen()

    {

        //save the controls configuration

        MappedAction action;

        for (int count = 0; count < _actions.Count; count++)

        {

            action = _actions[count];

            action.ActionControl = (Control)_selectedIndices[count];

        }

        ((IInputSystem)ScreenManager.Game.Services.GetService(typeof(IInputSystem))).MappedActions = _actions;

        base.ExitScreen();

    }

}

Code Listing 55 – ControlsScreen Class

We now have a way to control our character. The last system we’ll be adding is audio to handle sounds and music.

 


Scroll To Top
Disclaimer
DISCLAIMER: Web reader is currently in beta. Please report any issues through our support system. PDF and Kindle format files are also available for download.

Previous

Next



You are one step away from downloading ebooks from the Succinctly® series premier collection!
A confirmation has been sent to your email address. Please check and confirm your email subscription to complete the download.