Input system

Coordinator
Jan 1, 2008 at 11:43 PM
Sturm, I'll begin on the gamepad input handling tomorrow night (Jan 2nd). Just thought I'd let you know.
Jan 2, 2008 at 6:13 AM
Sounds good, have a look at the keyboard handler, then it shouldn't be a problem creating a handler for the GamePad. A few things I think you should consider
  • Use the next thousnads range (I think it's 3000) for the GamePad messages
  • Create messages for down/up/pressed keys (Similar to the keyboard messages)
Coordinator
Jan 5, 2008 at 7:39 AM
I'm working a couple of small fixes in the terrain system so that it is "ready". I will probably wait for Shaw to apply your input patch before I start coding a whole lot for the gamepad. Gamepad shouldn't take me too long, which is good because I've been insanely busy lately :o/
Coordinator
Jan 10, 2008 at 8:57 PM
Sorry I haven't been on the forums or contributing much over the last week, I've been insanely busy with work. However I plan on getting down to business finally tonight with the gamepad code. Anything new I shouldn't know about before I start?
Jan 10, 2008 at 9:18 PM
Edited Jan 10, 2008 at 9:19 PM
I've just uploaded a patch (675) with the mouse code, have a look at that for inspiration as I guess, it will be similar. Any discussions related to the patch should be done here: Patch 675 - Input+Unit+Fix
Coordinator
Jan 11, 2008 at 5:25 AM
Edited Jan 11, 2008 at 5:27 AM
I'm confused about some input stuff. Why are we sending messages for everyone button the is or isn't being pressed? Couldn't the input interface for a keyboard simply keep track of buttons that are down for the last and current frames. Then when a part of the game wanted to use input it would send a message to the input interface asking if the key is down, or if it is being held (down this frame and last), or if it is up (which would simply mean false on the down message).

For example, let us say I want to control a camera using W, A, S, and D. If I'm touching the W key, but not in control of the camera or anything that requires the keyboard, then I have no purpose in sending a message. But if I was in control of the camera, the camera could send a message to the input interface asking if W was being pressed, and if so, act on it. What if the player is holding in multiple keys, but only one of them does anything? No need to send 4-5 key messages each frame when we only care about 1 button.

Just as the camera interface controls all information about the cameras, the input interface I believe should hold all information about keys, buttons, mouse/joystick movement, etc. And if anything wants input info it should query the authority on input (the input interface).

Of course, I may just be looking at this wrong. All I see is that we're sending messages for all buttons, rather than a list of buttons and keys being filled, and then a message querying those instead.
Coordinator
Jan 11, 2008 at 5:32 AM
In fact, as a gamepad, there is no need to even fill a list. Each frame you simply update the gamepad states. The only time you even need to check a button to see if it is pressed, or being held, is if the gamepadhandler recieves a message requesting gamepad info. Otherwise it is just a waste.
Coordinator
Jan 11, 2008 at 5:46 AM
Edited Jan 11, 2008 at 5:47 AM
It looks like to me that all messages are going to a central place. How are they filtered after that? I see a message type, I'm assuming that is part of the answer. I may have to do some a little research to learn the system.

I've started designing the gamepad class and I'm wondering how someone would send a message to it, and how I would handle it once it got there?
Coordinator
Jan 11, 2008 at 6:14 AM
I see now that the messages are being sent. For example the free camera is recieving the messages for input.

I would highly prefer it be the other way around, where the camera can query for input.

Imagine this scenario:

The 360 gamepad has 9 buttons, 2 thumbsticks (each with x and y axis values), 2 triggers with float values, and the triggers can be pressed as well.
A system that fully utilizes the gamepad will have to check the up and down states of all of those each frame, and handle all of those cases each frame.

I see this as a minimum of:
1 message for every button pressed this frame.
1 message for every button not pressed this frame.
1 message for every button being held down.
(Minimum of 9 message per frame for buttons, maximum of 18 messages per frame)
1 message for each axis of each of the 2 thumbsticks ( 4 messages )
1 message for each trigger ( 2 messages )

Here's some very pseudocode of how I imaging it being insanely simple.

Create a message type for buttons.
Button message holds two bools: First bool is true/false for if button is up/down, second bool is true if button was down last frame.

Create the message, leave it empty, send it to the main message interface.
Gamepadhandler handles the message, fills the two bools with their proper info.
Message doesn't need to be sent back, because the sender of the message still knows the memory address of that information.
The message sender now has the proper information about that button.

This requires the gamepadhandler to only check that button, and only when queried. All gamepadhandler has to do each frame is update its previous and current states for any connected gamepads.

Now we're down from 15-24 messages per second, to a minimum of zero, and a maximum of the number of things we wish to query, which at the max, is 13 I believe. This substancially cuts down on code as well. If I send out the 15-24 messages each frame I'll also have to check each buttons up and down, and send messages, then check the triggers, and send messages, then check the thumbsticks, and send messages.

Here's some actual code I was imagining:

public class GamepadHandler : InputHandler
    {
        private GamePadState[] currentGamePadStates;
        private GamePadState[] previousGamePadStates;
 
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="game">The <see cref="QSGame"/> instance for the game</param>
        public GamepadHandler(QSGame game)
            : base(game)
        {
            currentGamePadStates = new GamePadState[(int)PlayerIndex.Four];
            previousGamePadStates = new GamePadState[(int)PlayerIndex.Four];
        }
 
        /// <summary>
        /// Updates the gamepad state, retriving the current state
        /// </summary>
        /// <param name="gameTime">Snapshot of the game's timing state</param>
        protected override void UpdateCore(GameTime gameTime)
        {
            for (int i = 0; i < (int)PlayerIndex.Four; i++)
            {
                if (GamePad.GetState((PlayerIndex)i).IsConnected)
                {
                    CheckGamePad(i);
                }
            }
        }
 
        /// <summary>
        /// Setup gamepad states.
        /// </summary>
        /// <param name="gameTime">XNA built-in time parameter for timing</param>
        private void CheckGamePad(int pIndex)
        {
            PlayerIndex currentPlayer = (PlayerIndex)pIndex;
            currentGamePadStates[pIndex] = GamePad.GetState(currentPlayer);
 
            previousGamePadStates[pIndex] = currentGamePadStates[pIndex];
        }
 
        // These may just take in a message of the specific type, like a gamepadbuttonquery message. That
        // message might have a button in it, which could be filled with information.
        private void CheckButtonState(GamePadButtons button)
        {
            // Should fill incoming button message with:
                // Is button up or down?
                // Was button up or down last frame?
        }
 
        private void CheckTriggerValue(GamePadTriggers trigger)
        {
            // Should fill incoming button message with:
                // Value of trigger, a float value from 0 - 1.
        }
 
        private float CheckJoystickValue(GamePadThumbSticks thumbstick)
        {
            // Should fill thumbstick info with:
                // X and Y values of the thumbstick ( -1 - +1 ).
 
            return 0.0f;    // Temp
        }
    }

That is the entire class, and it wouldn't get much bigger than that.
Jan 11, 2008 at 9:19 AM
Actually that was my initial suggestion (suggested here: Engine structure and design) but Shaw, and somewhere along the line I think you too, argued against it, and since I couldn't convince you we ended up with the exception model.

If you think this was an error, then we need to rework this before going 0.19, which might mean that we need to postpone a weeks time. I can't say how much time I'll have during this weekend.
Jan 11, 2008 at 1:47 PM
Exception model? Man, it's way too early in the morning to be thinking about this stuff!

The problem with filtering each message that I saw was the sheer amount of the filtering that would need to occur. If each possible messaging end-point had to register for discontinuous ranges of messages, the overhead of filtering would most likely be more than just passing the message reference around to each end-point and letting the end-points switch on the message type. What I also suggested (I'm not sure if it was in that thread) was to add a "special case" handler for input. Somewhere in the middle there would be an input translator that would take input events and convert them to action events. For instance, it would hold a key mapping. This could be queriable for input events.

It would look something like:

public override void MyGame.Initialize()
{
    this.InputMapper.BindKey(Keys.W, (int)MyActions.MoveForward);
    this.InputMapper.BindKey(Keys.S, (int)MyActions.MoveBackward);
    this.InputMapper.BindGamePadButton(GamePadButton.A (int)MyActions.Use);
    ....
}
 
 
public void SomeScriptProcessingFunction()
{
    if(this.InputMapper.WasEventTriggered((int)MyActions.Use))    // True if and only if input button was pressed this frame, not held
    {
        // Fire weapon, open door, etc.
    }
    if(this.InputMapper.IsEventActive((int)MyActions.MoveForward))  // True if input button was pressed this frame or still being held from previous frame
    {
         // Move player forward
    }
}

Or you could assign C# events to the input mapper and let others bind to those events, but querying seems a little more elegant and efficient. The method names are kind of bogus, but I'm tired.

The motivation for this is you typically don't want game logic hard-coded to specific keys, and this provides a nice way to keep the messaging efficient and consistent, while also giving end users a better experience.

Now, on the messaging side of things, I would highly recommend putting as much data in a single message as possible. To this end, I would send one message per device, not per button.
Jan 11, 2008 at 3:53 PM
The query model mucks up my netorking implementation. I basically work off the fact messages are non-specific at the moment, and transmit all messages. What about having a player class as an intermidiatry between the various systems and the network. Local player gets data from the handlers, passes it onto the scene graph (or renderer) and the network when needed, and remote player is updated by the network and queried by the scene graph. Or, wild possibilty, should the netwok be tied to the input system? Im fine withwhatever though, as networking is a lot less important than an efficient input system.
Jan 11, 2008 at 5:49 PM
Networking has a higher importance than input system,if I hadto choose where to optimize I would always choose network. It's ok to have a few ms latency on each keypress (there won'tbe that many) but it's not ok to waste those in network code, as networking will send much more data than input ever will.
Jan 11, 2008 at 6:15 PM
True, efficiency is more important for netowkr than input. I only meant if our input system is inefficient, these inefficiences will be passed on to the network. I thinking more in terms of bandwidth than lag.
Coordinator
Jan 11, 2008 at 7:23 PM
Well we wouldn't have to re-route the messaging. We still send all messages to a central locations, we'd just have the gamepadhandler listen in for gamepad messages.

How is this any different than the way freecamera listens in on keyboard messages?
Jan 11, 2008 at 7:30 PM
I may be misunderstanding. The way youve suggested is the camera calls the keyboard which updates it handler, whihc the camera then accesses directly. This gives the network no way of knowing if it has been updated. Have I got the wrong end of the stick?
Jan 11, 2008 at 8:50 PM
First, I'm not a huge fan of the systems calling into each other like that.

Second, why does the network care about input messages?
Coordinator
Jan 11, 2008 at 9:29 PM
Edited Jan 11, 2008 at 9:30 PM
The camera doesn't call the keyboard, it simply listens for messages of keyboard type. At least that is what I gathered.

Feel free to take a gander at FreeCamera.cs. I'm not around code today so I can't remember much more than that.

Shaw, networking will often take character input and use that to determine where a character "should" be. This may imply it listens to messages for input.
Jan 11, 2008 at 9:45 PM

Shaw, networking will often take character input and use that to determine where a character "should" be. This may imply it listens to messages for input.


That information should come from the scripting/game logic component, not the input system.
Jan 11, 2008 at 10:45 PM
Just to ramp up some clarifications:

In a normal 1st/3rd PS game, the main character class (which is a class done by the game developer not us) would listen for player input. The camera doesn't do anything due to player input. When the player character moves the attached camara moves along the character. When the player characters head turns the camera turns as well, because it's attached to the head. In fact there is no difference between a camera and a hat in that sense.

This is of cause a simplified version of what most likely is going to be in a real game, but conseptually that should cover it.

It doesn't matter if the actual implementation uses events or a poll mechanism, the above scenario would be the same. Now we have two possibilities:
  • Events
    • Are standard C# syntax and most devs know how to react to them.
    • Do add some overhead as all messages are sent to all listeners
    • Listeners need to filter for their messages
    • Usually involves typecasting
  • Poll
    • Not a standard C# concept, but known from other areas (polling queues, etc)
    • You need to poll for every type of message you support
    • Needs more filtering on the engine side

My initial suggestion was to provide a poll mechanism, not only for keyboard but for all messages. This of cause only works if update isn't one of the messages. I would hate to implement two systems poll for input and events for the rest, as this would just add to the learning curve of the engine. But if you think this is needed I'll do that.
Jan 11, 2008 at 10:48 PM
Shaw, you could also have two maps, one which maps from Key to event and one that maps from event to delegate. This way you would omit the big ugly if statement.
Coordinator
Jan 11, 2008 at 11:02 PM
Depends on your camera. In WoW you can optionally move your camera around the player, and it is based solely on input. In games like Zelda for the N64 the camera was "lazy" and opted only to move when it needed to, unless you told it to snap behind you. The camera would actually act detached from the player for the most part, it allowed you to walk a certain distance away from or towards it.
Jan 11, 2008 at 11:19 PM
This illustrates another way of binding key/mouse/gamepad/etc to actual game methods:

    // Just message types some defined in the engine others in the game code
    public static class MessageType
    {
        public const int MoveForward = 1000;
        public const int MoveBackward = 1001;
    }
    
    // Delegate used when invoking mapped messages
    public delegate void GameTargetDelegate();
 
    public class MyGame
    {
        public void Initialize()
        {
            this.BindKey(Keys.W, MessageType.MoveForward);
            this.BindKey(Keys.S, MessageType.MoveBackward);
        }
 
        public void BindKey(Keys key, int messageType)
        {
            // Do the binding
        }
 
        public void BindEvent(int messageType, GameTargetDelegate target)
        {
            // Do the event binding
        }
    }
 
    public class Player
    {
        private MyGame Game;
 
        public void InitializeCore()
        {
            this.Game.BindEvent(MessageType.MoveForward, new GameTargetDelegate(this.MoveForward));
        }
 
        public void MoveForward()
        {
            // Translate the character one step forward
        }
    } 
Of cause this doesn't solve how the keyboard messages are send, only gives a possible solution on how to map these to methods
Jan 11, 2008 at 11:27 PM

LordIkon wrote:
Depends on your camera. In WoW you can optionally move your camera around the player, and it is based solely on input. In games like Zelda for the N64 the camera was "lazy" and opted only to move when it needed to, unless you told it to snap behind you. The camera would actually act detached from the player for the most part, it allowed you to walk a certain distance away from or towards it.


In WoW as soon as you release the left mouse button and are moving the camera snaps right in behind you again. Yes you can combine all types of camera to achieve the result you want. My point was to show that usually it's not the camera which moves, but the player (as it's the case in WoW/Zelda and many others) though there are certainly games where the camera is moved independently of the character(s) Civilization/Dragonshard/Warcraft/C&C. Others even delegates keyboard input to the currently selected object while allowing free movement. So there are many stories for doing this, but most of these are defined in the game code, and we should just deliver the base code for it.
Jan 12, 2008 at 6:43 PM
Edited Jan 12, 2008 at 6:53 PM
I've modified the input code a bit, I've added the following check to InputManager's UpdateCore:

            // Do not update the any handlers if the game does not have focus
            if (this.Game.IsActive == false)
            {
                return;
            } 
We should consider a more generic way of doing this as only a few systems should update while the game does not have focus.

I've also added code to the MouseHandler which will hide the cursor on activate and show the cursor when the game looses focus. This should help when used inside the editor as well.