Dealing with Input

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • Dealing with Input

      What is the best way to deal with input and multithreading?

      I was thinking of having a class that would store bool vars for each key (up=false, down=true) and having a processing flag that would be used to bypass winProc input updates when the input class is being accessed by another part of the program. Does this sound like a good plan?

      The post was edited 1 time, last by Roster ().

    • RE: Dealing with Input

      The first thing you want to do is separate the concept of hardware buttons from software commands. There should be a data file (XML or otherwise) that maps hardware buttons (like the left mouse button or the space bar or even a gamepad button) to some game command (like "MoveForward" or "Shoot"). This is extremely important for allowing you to easily support many different devices and key configurations. Game commands are then sent with events and any system that needs to respond to input receives that event.

      There are many different ways to do this. I wrote the input handling scheme for Rat Race and adapted that same methodology for my own games, so I'll explain how that works. Keep in mind that this is just one way of dealing with it.

      My input configuration is handled with a lua file, which I typically call input.lua. This file contains the key configuration definition table. An example of this table might look like this:

      Source Code

      1. local inputMap =
      2. {
      3. default =
      4. {
      5. Esc = { "Event", "QuitGame" },
      6. },
      7. Run =
      8. {
      9. Up = { "Event", "MoveUp" },
      10. Down = { "Event", "MoveDown" },
      11. Space = { "Script", "FireWeapon" },
      12. },
      13. MainMenu =
      14. {
      15. Space = { "Script", "StartGame" },
      16. },
      17. };
      Display All


      The outer tables define input modes and the inner tables are the key bindings for those modes. Thus, I have defined two modes, one called Run and the other called MainMenu (default is special; I'll talk about that later). When in the Run mode, Up, Down, and Space are hardware buttons bound to "MoveUp", "MoveDown", and "FireWeapon", respectively.

      The other part of the table can either be "Event" or "Script". "Event" will fire off an event in C++ and "Script" will directly call a global Lua function. This is done partially for performance reasons and partially for convenience.

      Notice how the space bar is bound in Run and MainMenu. Since we can only ever be in one mode at a time, this is perfectly valid. The default table is a special case. Any key there is always evaluated regardless of the input mode unless it is explicitly rebound somewhere else.

      During game initialization, Lua calls the InitInputMap() function, which is a C++ function that is exposed to Lua. This sends the table to the C++ InputMgr, which loops through all the tables here and builds up an internal mapping to make processing more efficient. This way, anything that's tagged with "Event" will never touch the scripting system at all. This is important for input events that change constantly, like MouseMove. Having the "Script" tag is also very useful because I don't want Lua responding to the C++ input event. Having that clear separation is important. I can also add new input handlers without recompiling the game since it lives 100% in Lua.

      Regarding your multi-threading concerns, my input system is not thread-safe at all. It doesn't need to be. My engine has three threads: the main thread processes all the game logic (include input), another thread deals with rendering, and a third thread deals with resource loading. My render thread and resource loader have absolutely no knowledge of my input system nor do they need it. All input processing happens on the main thread.

      -Rez