How to manage input from more than one potential source?

Started by
8 comments, last by haegarr 11 years, 6 months ago
I am trying to put together a thorough input system, and I can't seem to work it through in my head (or in code).

Say for example, I want to use x number of keys, or mouse button(s) or joystick button(s) to perform a single continuous action, for example 'running forward'.

I can handle gathering the input, and I can handle associating the event with an action, but what I can't figure out, is the best way to cross-check for the current state of the 'other' inputs.

For discussion sake, lets say a key event would be something like... 'key_space', 'mouse_1', 'joy_1', followed by the appropriate data, such as 'true', 'false', .0121, etc.

So say I bound 'key_w', 'key_up', as well as 'mouse_2' to the action 'forward'.

So now if I press either of those keys, or the right mouse button, the 'forward' action will happen, meaning, the player will run 'forward'.

So now say I press all three, this works fine, because they all do the same function. But what happens when I let go of the right mouse button? In code, it would send through a 'mouse_2' event with the data 'false', which should cause the player to stop moving forward, unfortunately, both key_w, and key_up, are still down...

Is there any way to tackle something like this? Where I could have (n) number of inputs to handle a single thing like this? I know if I have a finite number of options, I could just check for the state of the keys when the 'up' event occurs... if(false == keys_w){/*stop*/}else{/*keep going*/}

I thought of using an integer for this, but that just seems sloppy... Like if I press key_w, held_amt++, if I press key_up, held_amt++, if I press mouse_2, held_amt++. Then when I release one of them, I do held_amt--, check if it is equal to zero, and then if it is, stop running...

I don't know, this just seems very sloppy to me.

On top of this, say I pressed forward, then back, then a different forward key. I would want it to respond properly to the currently held down keys, all of which could be reassigned and the amounts of them could be 1 to n.

Please help if you can! biggrin.png

By the way, I have already read through http://www.gamedev.n...stem-for-games/ which was informative and helpful, but it doesn't cover my issue. I have also tried looking up anything I can find in google, as well as on these boards, but most of them have to do with the lower level logic of how to just get multiple sources of input, rather than the higher functionality of how to orchestrate it.

EDIT: The only other method I was thinking of was polling the state of each potential input onUpdate, but I wanted use a callback system instead, onKeyChange... onKeyUp / onKeyDown type of thing.
Advertisement
You don't seem to have a problem here as much as you just seem to have a bad design. Don't handle everything at once, just handle them separately.

Check the keyboard -- if a key is down, take the appropriate action in your program.
Check the mouse -- if a button is down, take the appropriate action in your program.
Check the gamepad -- of a button is down, take the appropriate action in your program.

Do this in a generic way, and just loop over all valid input methods. Better yet, if it's a game, let the player choose the preferred input method, and check only that. With the exception that you always want to check the keyboard for important keys like ESC or whatever else.

I have a Pen + Touch Wacom tablet plugged in, (like many people) and I don't expect any programs to account for it separably from my mouse. If I move my finger or pen on my tablet while moving my mouse, I'm going to get messed up cursor navigation. Especially since they both handle cursor movement very differently! There is no expectation of me (or any other user) that I should be able to make use of both at the same time.
I thought of using an integer for this, but that just seems sloppy... Like if I press key_w, held_amt++, if I press key_up, held_amt++, if I press mouse_2, held_amt++. Then when I release one of them, I do held_amt--, check if it is equal to zero, and then if it is, stop running...

I don't know, this just seems very sloppy to me.[/quote]
Doesn't seem that sloppy to me given what you want to do.

What you could do is have an enum of possible actions (Action) mapped to an integer to count the number of inputs that are applied to that action like this:
std::map<Action, int> ActionBindings;
And for the callback system you want to associate a key with an action in another map like this:
std::map<Key, Action> KeyBindings;

When OnKeyUp/Down is called, map from the Key to the Action and then from the Action to the count for that Action. Then in the part of the code that performs actions just check for which actions are currently occurring and do actions based on that. Hope this helps.

C++: A Dialog | C++0x Features: Part1 (lambdas, auto, static_assert) , Part 2 (rvalue references) , Part 3 (decltype) | Write Games | Fix Your Timestep!

Oh if you need ALL keys to be applied then you'll need to keep track of that count as well. I'd do this by modifying the type of ActionBindings map to use a structure linstead like this:

struct KeyCount
{
int NecessaryCount;
int CurrentCount;
};

Then define ActionBindings like this instead:
std::map<Action, KeyCount> ActionBindings;
Then when you change the mapping of which keys go to which action you adjust NecessaryCount accordingly.

C++: A Dialog | C++0x Features: Part1 (lambdas, auto, static_assert) , Part 2 (rvalue references) , Part 3 (decltype) | Write Games | Fix Your Timestep!

You had it right the first time nobodynews, it looks like the integer approach will be what I'll go with.

Daaark, you hit the nail on the head with your example. Say I do have both a pen and a mouse attached (for some crazy reason). I want to be able to take either a pen movement or a mouse movement, and apply both to the same action, but when one stops, and the other does not, I need a way to determine that:

1. The pen stopped.
2. The mouse is still moving.
3. Reflect this within the game.

By using nobodynews example, it would go something like...

1. Start moving mouse along the positive x axis, the mouse_px event will be triggered with a '1' value passed in (just as an example).
2. The px_axis_int will be incremented by +1.
3. Start moving pen or finger also along the positive x axis, the pen_px or finger_px event will be triggered with a '1' value passed in.
4. The px_axis_int will be incremented by +1 for the pen and or finger.

Since all three of these actions 'mouse_px', 'pen_px', and 'finger_px' are tied to the same event... uh... 'move_right', lets say... dVal.x = 1.0 So now the character starts moving along the x axis.

Now say I stop moving the mouse, I'll have to set px_axis_int -=1, then check if (px_axis_int == 0){dVal.x = 0.0}. I would do this for each input assigned to this action.

Unless someone has a better way to accomplish this? Of course these are just obscure examples, but I just like to cover all my bases... Most people wouldn't assign 'W', 'Up Arrow', 'Right Mouse Button', 'Joystick Button 8', all to the same action, and use them interchangeably, but 'what if'? I just want a system that works for whatever the user wants to end up doing...

So say I bound 'key_w', 'key_up', as well as 'mouse_2' to the action 'forward'.

So now if I press either of those keys, or the right mouse button, the 'forward' action will happen, meaning, the player will run 'forward'.

So now say I press all three, this works fine, because they all do the same function. But what happens when I let go of the right mouse button? In code, it would send through a 'mouse_2' event with the data 'false', which should cause the player to stop moving forward, unfortunately, both key_w, and key_up, are still down...

Is there any way to tackle something like this?


Hey :) I myself usually do this:

I have an array of "actions" associated with keys, like "run forward", "jump", etc. And I do have several "keysets" which are bound to same "actions".
Something like:

{
"Jump": ['space', 'button 1'] // "action": [set_1, set_2]
"Run": ['shift', 'button 9'] // "another action": [set_1, set_2]
...
}

So, when I press any button, from any input, my engine sends an "action" event, associated with this button. And with the event I also send the index of the "set", which activated this event. For example, if I press "space" on my keyboard it sends something like: (action: "jump", keyset: 1) and when I press "button 1" on my gamepad, it sends the same "action-event", but with different keyset index (2 in this case). And now my InputManager can figure out which key was pressed and if I will press down "shift" AND "button 9", and release "shift" after that, InputManager would know, that the released button was from another set and will not switch off the current "action".
slmgc, From what you are describing, it sounds like you are putting your actions into a queue or list of some sort, is that safe to assume? Then when you 'remove' or cancel the action, it removes only the one associated to that set or index.

It is an interesting approach, it sort of turns duplicate actions into unique actions via your indexing (set) system.

Thank you for sharing that with me.

slmgc, From what you are describing, it sounds like you are putting your actions into a queue or list of some sort, is that safe to assume? Then when you 'remove' or cancel the action, it removes only the one associated to that set or index.

It is an interesting approach, it sort of turns duplicate actions into unique actions via your indexing (set) system.

Thank you for sharing that with me.


You can use bitwise operations on the "action" flag, for example: you have two keysets (1 and 2). Action flag will be 0 when no associated keys were pressed. When an action event comes (from keyset 2, for example), you set a bit flag with an index of the keyset for this action (so the action flag becomes 0010). When comes this action-event again, but with another keyset (1), you set a different bit (so now you flag is 0011). When a user releases a pressed key, you receive a reset event for this action for specific keyset (let it be 2), so you reset a specific bit of the action flag (it becomes 0001). When a user releases all pressed keys on all of the input devices, you just reset all of the bits of the action flag (so it becomes 0 again). The main plus of this approach is that you just have to compare if a specific action flag is zero or not, if not, then some key (associated with this action) from one of the input devices is pressed, if the flag becomes zero, then this action ends.

Hope this helps ;)

  1. Split inputs and actions, use "binds" to connect them
  2. Pool input states (for each input device), keep track of previous and current state, based on this you can handle the difference (press, holding, release)
  3. Based on input states, generate game actions, feed those to game logic

Then it becomes something like this:

// read device
for each input device
loop over every possible input (key, button, axis)
get current value (0.0-1.0), mark current time (useful for various things)
// input 2 action
for each action
for each bound input
action_value = max( inputdevice.currentvalue[bound_input], action_value );
// action logic
for each action
if action_value > prev_value && prev_value == 0 -> action was "pressed"
if action_value == 0 && prev_value > 0 -> action was "released"
if action_value > 0 && prev_value > 0 -> action is "held"
prev_value = action_value;


This works nice even for analog values; instead of making IsAction* queries, you can say GetActionValue and return the current action value (0.76 from gamepad analog stick). By tracking time on action, you can even detect difference between fast or long button press.
It is IMHO wise to map input to actions with the sense to separate both subsystems. On the action side the things should be unique. Hence a way of making multiple equivalent input unique is needed.

When looking at different input methods it becomes clear that the first step would be in transforming input to meaningful values. E.g. do you need a trigger, then a key released-to-pressed transition may be the choice. If you need a temporary boolean state, then the key pressed state may be a choice. When you need a stable state, then you can use toggling by key presses of the same or even 2 different keys. As a strange example, you can transform a mouse move into a trigger when detecting a move by at least L units in horizontal direction. However, in the OP you want to have a temporary boolean state from key presses.

If all input sources for a given task are "normalized" in the above sense, functions may be used to mix different input signals. Typical functions would be min(...), max(...), and perhaps mean(...). E.g. applying min(...) to N state signals means that all of the signals need to be 1 to generate a 1 as output; similarly applying max(...) to N state signals means that any of the signals causes 1 as output. This may be implemented using an enum or using an approach with counting and comparing against a threshold. In the OP you want to use the min(...) function.

This topic is closed to new replies.

Advertisement