16
\$\begingroup\$

Is there a standard technique for managing input in large games. Currently, in my project, all input handling is done in the game loop, like so:

while(SDL_PollEvent(&event)){
            switch(event.type){
                case SDL_QUIT:
                    exit = 1;
                    break;
                case SDL_KEYDOWN:
                    switch(event.key.keysym.sym){
                        case SDLK_c:
                            //do stuff
                            break;
                    }
                    break;
                case SDL_MOUSEBUTTONDOWN:
                    switch(event.button.button){
                        case SDL_BUTTON_MIDDLE:
                                //do stuff
                                break;
                            }
                    }
                    break;
            }

(I am using SDL, but I expect the main practice applies libraries and frameworks as well). For a large project this doesn't seem the best solution. I may have several objects all wanting to know what the user has pressed, so it would make more sense for those objects to handle input. However, they can't all be handling input, as after one gets an event, it will be pushed off the event buffer, so another object won't receive that input. What method is most commonly used to counteract this?

\$\endgroup\$
  • \$\begingroup\$ With an event manager, you could fire event in input and let all other parts of your game register to them. \$\endgroup\$ – danijar Jul 22 '13 at 17:11
  • \$\begingroup\$ @danijar what exactly do you mean by an event manager, is it possible if you could provide some skeleton pseudo code to show what kind of thing you are talking about? \$\endgroup\$ – w4etwetewtwet Jul 22 '13 at 17:14
  • 3
    \$\begingroup\$ related: gamedev.stackexchange.com/questions/48315/… gamedev.net/blog/355/… \$\endgroup\$ – Patryk Czachurski Jul 22 '13 at 17:29
  • 1
    \$\begingroup\$ I wrote an answer to elaborate on event managers, which are the way to go for input handling for me. \$\endgroup\$ – danijar Jul 23 '13 at 8:57
12
\$\begingroup\$

Since asked by the thread starter, I elaborate on event managers. I think this is a good way to handle input in a game.

An event manager is a global class which allows to both register callback functions to keys and to fire off those callbacks. The event manager stores registered functions in a private list grouped by their key. Each time a key gets fired, all registered callbacks are executed.

The callbacks could be std::function objects which can hold lambdas. The keys could be strings. Since the manager is global, components of your application can register to keys fired from other components.

// in character controller
// at initialization time
Events->Register("Jump", [=]{
    // perform the movement
});

// in input controller
// inside the game loop
// note that I took the code structure from the question
case SDL_KEYDOWN:
    switch(event.key.keysym.sym) {
    case SDLK_c:
        Events->Fire("Jump");
        break;
    }
    break;

You could even extend this event manager to allow passing values as additional arguments. C++ templates are great for this. You could use such a system to, say, for a "WindowResize" event pass the new window size, so that listening components don't need to fetch it themselves. This can reduce code dependencies quite a bit.

Events->Register<int>("LevelUp", [=](int NewLevel){ ... });

I've implemented such an event manger for my game. If you are interested, I'll post the link to the code here.

Using an event manager, you can easily broadcast input information within your application. Moreover, this allows for a nice way to let the user customize key bindings. Components listen to semantic events instead of keys directly ("PlayerJump" instead of "KeyPressedSpace"). Then you can have an input mapping component that listens for "KeyPressedSpace" and triggers whatever action the user bound to that key.

\$\endgroup\$
  • 4
    \$\begingroup\$ Great answer, thanks. Although I'd love to see the code, I don't want to copy it, so I won't ask you to post it until I've implemented my own, as I'll learn more that way. \$\endgroup\$ – w4etwetewtwet Jul 23 '13 at 10:12
  • \$\begingroup\$ I just thought of something, can I pass any member function like this, or won't the register function have to take AClass::func, limiting it to one classes member functions \$\endgroup\$ – w4etwetewtwet Jul 23 '13 at 11:24
  • \$\begingroup\$ That's the great thing about lambda expressions in C++, you can specify a capture clause [=] and references to all local variables accessed from the lambda will be copied over. So you don't have to pass a this pointer or something like this. But note that you can't store lambdas with capture clause in old C function pointers. However, the C++ std::function works fine. \$\endgroup\$ – danijar Jul 23 '13 at 17:45
  • \$\begingroup\$ std::function is very slow \$\endgroup\$ – TheStatehz May 19 '17 at 21:16
22
\$\begingroup\$

Split this into several layers.

At the lowest layer you have raw input events from the OS. SDL keyboard input, mouse input, joystick input, etc. You might have several platforms (SDL is a least-common-denominator lacking several input forms, for instance, which you might later care about).

You can abstract these with a very low-level custom event type, like "keyboard button down" or the like. When your platform layer (SDL game loop) receives input, it should create these low-level events and then forward them on to an input manager. It can do these with simple method calls, callback functions, a complicated event system, whatever you like best.

The input system now has the job of translating low-level input into high-level logical events. Game logic does not at all care that SPACE was pressed. It cares that JUMP was pressed. The input manager's job is to collect these low-level input events and generate high-level input events. It is responsible for knowing that the spacebar and the 'A' gamepad button both map to the logical command Jump. It deals with gamepad vs mouse look controls and so on. It emits high-level logical events that are as abstract as possible from the low-level controls (there are some limitations here, but you can abstract things away completely in the common case).

Your character controller then receives these events and processes these high-level input events to actually respond. The platform layer sent the event "Key down spacebar." The input system received that, looks at its mapping tables/logic, and then sends the event "Pressed jump." The game logic / character controller receives that event, checks that the player is actually allowed to jump, and then emits the "Player jumped" event (or just directly causes a jump to happen), which the rest of game logic uses to do whatever.

Anything dependent on game logic goes into the player controller. Anything OS dependent goes in the platform layer. All the rest goes into the input management layer.

Here's some amateurish ASCII art to describe this:

-----------------------------------------------------------------------
Platform Abstraction | Collect and forward OS input events
-----------------------------------------------------------------------
                          | |
                          | |
                         \   /
                          \_/
-----------------------------------------------------------------------
    Input Manager    | Translate OS input events into logical events
-----------------------------------------------------------------------
                          | |
                          | |
                         \   /
                          \_/
-----------------------------------------------------------------------
Character Controller | React to logical events and affect game play
-----------------------------------------------------------------------
                          | |
                          | |
                         \   /
                          \_/
-----------------------------------------------------------------------
      Game Logic     | React to player actions and provides feedback
-----------------------------------------------------------------------
\$\endgroup\$
  • \$\begingroup\$ Cool ASCII art, but not that necessary, I'm sorry. I suggest using a numbered list instead. Anyway a good answer! \$\endgroup\$ – danijar Jul 23 '13 at 9:03
  • 1
    \$\begingroup\$ @danijar: Eh, I was experimenting, had never tried drawing an answer out before. More work than it was worth, but a lot less work than dealing with a paint program. :) \$\endgroup\$ – Sean Middleditch Jul 23 '13 at 16:09
  • \$\begingroup\$ Alright, understandable :-) \$\endgroup\$ – danijar Jul 23 '13 at 17:33
  • 8
    \$\begingroup\$ Personally, I prefer the ASCII art way more than a boring numbered list. \$\endgroup\$ – Jesse Emond Jul 23 '13 at 21:26
  • \$\begingroup\$ @JesseEmond Hey, here for art? \$\endgroup\$ – danijar Jul 27 '13 at 22:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.