Handling Input

Started by
15 comments, last by Zipster 9 years, 5 months ago

Hi. I'm relatively new to more serious game programming, having made some toy stuff in the past using various languages and frameworks, but I'm trying to learn more about game architecture. Right now I'm trying to wrap my head around how input is handled in various games. I realize this may change depending on the game and even hardware being used, but any insight is appreciated.

I'm currently using Love2D and Lua, but the examples I've provided are in Python (for clarity).

When I started writing my game, I was polling for user input during the update interval from each object. So I had something like this:


while True:
    for object in game.object_list:
        object.update()

I've omitted some things, but I'll add that I'm using a fixed timestep.

There were a few problems with this approach:

1) If the user changed input, such as released a key, mid-update, some objects might react as though the key were pressed while others would not. This effect is even more noticeable when there is more going on in the update loop.

2) It is possible that the user does something like presses and releases a key before input is even checked, in which case that input is essentially lost completely.

The solution to the first problem was to check for input once at the start of the update loop (say by storing it in an array), and then polling that array while updating game objects rather than checking the input directly from each game object.


while True:
    InputManager.update()
    for object in game.objects:
        object.update()

This still has the second problem though.

Thus, I started looking at the callback functions more closely. I am giving the following: key_pressed, key_released, mouse_pressed, mouse_released.

After doing some research, it seems the best way to handle input is to push events into a queue when they arise and then empty that queue during fixed update calls. The callback functions fire every time the corresponding event occurs, so I would push events onto the queue from them.


def key_pressed(key):
    InputManager.queue.push(KeyDownEvent(key))

Now we get to the meat of my question: what do I do with this queue? There are a few ideas that occur to me and none seem correct. E.g.


while True:
    while InputManager.queue.has_next():
        for object in game.objects:
            object.handle_input(InputManager.queue.next())

    for object in game.objects:
        object.update()

I guess my question is: how do I cleanly handle input for my game once I've captured it and pushed it into a queue?

Advertisement
Does every game object need to handle every keypress? I'd consider mapping the key presses to valid actions (which would allow for configurable controls), and then iterating through the action queue.

Game objects can then either register themselves or be registered by a factory to listen for when an action is broadcast. This could be done action by action or actions could be grouped into types and game objects could listen for specific types of actions.

Also,http://www.gamedev.net/blog/355/entry-2250186-designing-a-robust-input-handling-system-for-games/ might give you some ideas.

Game objects don't necessarily need to handle every keypress, no. I just haven't found any actual examples of it being done other ways. I've actually read that post, but I guess I didn't take much from it. My issue with a lot of resources is that they stick to very high-level language and I never finish reading it actually knowing how to implement anything.

So let's say we map keypressese to actions. A keypress goes into a queue (presumably), some sort of input handler figures out what it maps to (presumably), and we push these actions onto a different queue. Now when we go through the action queue we have things like "AttackButtonPressed" or whatever. I'm still not sure how to handle that in the game objects. And I'm not sure how to reconcile that with the update loop.

I'm not sure what you mean by factory and I'm not sure what registering themselves would be. I'm also not sure, again, how to reconcile that with the update loop and general game logic

Okay, I'm less familiar with lua than I should be, and I'm out of practice as well, so with that caveat...

The actions to be mapped shouldn't really correspond to how the input is received, but rather just to things the player can do in the game. So instead of "attackbuttonpressed" there would just be an "attack" action. You may also have actions such as moveleft, moveright, jump, useonject, useselectedinventoryitem, selectnextinventoryitem, selectpreviousinventoryitem. You get the idea.

By "factory" I mean an object or function whose purpose is to create certain objects. You may or may not find it helpful to have a GameObjectFactory.

By registering game objects I mean that, when a game object is created it can be added to a table of objects that will handle a given action. In lua, you can create callback functions so instead of having a table of objects that handle an action you can have a table of callbacks to iterate through and call.

So what I'm thinking is this:

For each action in your game you have a callback table. When a game object is created, you add a callback function for that object to the callback table for each action that object should handle. I'm thinking an object factory can help automate things here.

When a player presses a key, instead of just adding that keypress to an input queue, look up what, if any, action is mapped to that key and add that action to an action queue. (Maintaining both an input queue and an action queue would be redundant.)

Then during your update, iterate through the action queue, and for each action in the queue, iterate through its callback table calling each callback function in turn.

Does that make sense to you?

Thanks for the response.

Does it make sense to me? Eh, sort of. I'm not sure where my particular hangups are, but I'm having trouble seeing it all fit together in my head. In the past I've always had objects updating themselves and checking for whatever input they needed during the update. There are problems with that approach though. This is quite different, so I think it might take me a while before I actually understand what keys I need to press while in my IDE to make the program do what I want...

I'll have to think about it.

I think what's missing are the intermediary layers between actions and game objects. Ideally the game objects don't listen for actions themselves, but rather delegate the relationships between actions and behaviors to various systems, i.e. a movement system, camera system, etc. For instance, if the movement system receives a MOVE_LEFT action, it can contextually determine which objects need to "move left" and update them accordingly. At the end of the day, the game objects know nothing about actions or how they relate to their behaviors; all they know is that Something From Above wants them to move left.

Well, that only adds more layers of confusion to this for me. English is my first language, but maybe I need a good dictionary because I seriously have no clue what is being talked about when it comes to this stuff.

Assuming that you know what a thread is, why not call:

while ( !bQuitCond ) {
      m_kbKeyboardBuffer.Update( kKeyboard ); //Thread-safe this for the sake of non-corruption of data!


     if ( !m_psmStateMachine->Tick() ) {
          // Now I can get kKeyboard info and do something - re-map it, change the state, etc.
          break;
     }


    m_psmStateMachine->Draw();
}
...and make Keyboard and KeyboardBuffer classes?
I don't know nothing about your specific implementation, but you should search for Input Mapping (Platform -> Engine) and Input Re-mapping (Engine -> Game). Also, if you want input syncronization in your simulation you should time-stamp the keyboard events and take Time into consideration.
From what I am seeing with your implementation, the only thing I can say is: you're doing it wrong. There is two ways of solving your problems: keeping it simple and learning with your own solutions, or skip it and learn with the more experienced ones. InputManager is a very generalized class that knows what your fingers and your keyboard color are.

I find your variable names without any explanation to be quite confusing. It seems as though you are capturing the keyboard input, figuring out the state during each tick, and then using that, but I'm really not sure. !m_psmStateMachine->Tick()? I have no idea what this means without explanation.

If the above understanding is mostly correct, then that's already what I've been doing.

I have no idea what keyboard color is.

I recognize that I've missed the explanation and I am going to post for you.

1: psm: pointer to a state machine member (m_) if you're using C++. It could be just game.Tick/Update;

2: Tick: the updating of the Game. The m_psmStateMachine gets updated at syncronized steps like a clock;

3: InputManager is very generalized. It knows everything related to input. You're doing wrong;

4: A sarcasm with #3.

Also, you should not handle the input directly in the "GameObject". You should handle inside a Game State. Eg.:


if ( FOUND_AN_KEY_PRESSED_OR_AN_EVENT_THAT_I_WANT_TO_HANDLE) {

m_goSuperMan.GetPhysicalProperty()->MoveToPosition( x, y, z );

//OR:

m_goSuperMan.KryptoniteApproaching();
}

m_goSuperMan is an GameObject that it is stored inside the GamePlay class that has a physical property that can be updated by a physics system so you can interface with the GameObject via his physical properties without the Game Object knowing about InputManager.

This topic is closed to new replies.

Advertisement