Sign in to follow this  
Hurp

Unity Handling keyboard/mouse input

Recommended Posts

Hurp    106
"Input Managers" are something that I see are commonly discussed in this forum, to a create extent in this thread. However, I typically see only keyboard events discussed and not mouse inputs such as x/y movement, scrolling and extra buttons (such as thumb buttons like Mouse5). I am trying to venture away from always using DirectX input to handle keyboard/mouse. Can anyone suggest a good way to set up handling input for mouse event, or even more ways to do keyboard events.

Any kind of explanation, or source code or tutorials would be very much appreciated.

Share this post


Link to post
Share on other sites
Buckeye    10747
Quote:
I am trying to venture away from always using DirectX input..

Good idea.

Unless you run into a particular problem, just use window messages (WM_KEYDOWN, WM_MOUSEMOVE, WM_VSCROLL, etc.).

Share this post


Link to post
Share on other sites
Hurp    106
There is one issue that I can not grasp the concept of, and that is how "InputNotifiers" are used.

For example, I can see that typically when the "InputManager" detects that a key it pressed, it will send out a notifier. However, how is that notifier used? For example, is it attached to a class some how, or is an instance of it created and signed a function pointer it should call when a key is pressed?

Share this post


Link to post
Share on other sites
szecs    2990
I say forget about these fancy "notifier" "listener" fancy shit and the classes. Just make it work. Maybe with one nasty disgusting switch statement and globals and every evil things. Your next input handler will be much nicer.

Share this post


Link to post
Share on other sites
Buckeye    10747
An EventManager I wrote at one time maintained a vector of event/function pointer pairs. Events (with an accompanying callback function pointer) were registered (added to the vector) with the EventManager. On an event, the mgr iterated through the vector looking for requested events and calling the associated callback function.

I've never found a really good use for an event manager that complicated.

Similar to what szecs mentions, since then I just maintain a bunch of global booleans* for keystrokes of interest, and each object/class/function/etc. checks the globals as needed.

*E.g.,

case WM_KEYDOWN:
switch(LOWORD(wParam))
{
case VK_NUMPAD0: bNP0 = true; break;
case VK_NUMPAD1: bNP1 = true; break;
// etc.
case WM_KEYUP:
switch(LOWORD(wParam))
{
case VK_NUMPAD0: bNP0 = false; break;
case VK_NUMPAD1: bNP1 = false; break;
// etc.

where bNP0, bNP1, bNP2, etc., are just global bools.

Share this post


Link to post
Share on other sites
Hurp    106
I would to have my InputManager not deal with global variables.

Here is how I was going to set it up, first I create a class known as InputNotifier. This class will take in a key, a function pointer to a KeyDown function and a function pointer to a KeyUp function.


#define INFUNCPTR(name) void (GameObject::*name)()
class InputNotifier
{
protected:
InputKey m_Key;

INFUNCPTR(m_OnKeyUp);
INFUNCPTR(m_OnKeyDown);

public:
InputNotifier(InputKey key, INFUNCPTR(onKeyUp), INFUNCPTR(onKeyDown))
{
key = key;
m_OnKeyUp = onKeyUp;
m_OnKeyDown = onKeyDown;
}
virtual ~InputNotifier() { }

virtual void OnKeyDown(LPARAM lParam)
{
m_OnKeyUp;
}

virtual void OnKeyUp(LPARAM lParam)
{
m_OnKeyDown;
}

InputKey GetKey() const { return m_Key; }
};



The second thing I would do is create a manager, to use with WinProc


class InputManager
{
protected:
vector<InputNotifier *> m_InputListeners;

public:
InputManager();
virtual ~InputManager();
bool AddListener(InputKey key, INFUNCPTR(onKeyUp), INFUNCPTR(onKeyDown));

void OnKeyDown(WPARAM wParam, LPARAM lParam);
void OnKeyUp(WPARAM wParam, LPARAM lParam);
};



The problem I am having is how do I hook in a function that would deal with objects that update based upon time? For example, I want my W key to move my camera up a little but, but that would require the current time slice, or approaching time slice.

Possible Fix 1: (Hack)
In could fix this by storing the last time slice, then use that when I press the W key ... but this seems like a hack.

Possible Fix 2: (Hackish).
In my message manager I store a queue of messages that require the time slice to be added in. The problem with doing this is that it would be a seperate list of messages to all of my other messages, meaning they may not get executed in order ... this could be very dangerous.

Does anyone have any suggestions on how this may be done?

Share this post


Link to post
Share on other sites
Buckeye    10747
Quote:
I want my W key to move my camera up a little..

It's not very clear what problem you're trying to solve.

I would think, if the camera updates at some delta time, and W is down, move the camera at some speed times the delta time, regardless of when the key was pressed. Are you concerned that there will be some conflict (it's not clear) if the key was pressed just before the update so it really wasn't down for the entire delta time? Are your update times so long that it would make a difference?

Maybe a bit more explanation would help.

Share this post


Link to post
Share on other sites
Hurp    106
Sorry about that, basically, I want to some how communicate to an object (my camera) that W has been pressed so it can preform a certain function.

The problem I am having is syncing up input with my update loop.

The way I use to have my update look when the input manager was not done through WndProc but rather DirectX was basically like this ...

------ Main Engine Update

float dt = gameTimer.fromLastUpdate()

if ( InputManager.buttonPressed(w) == true)
camera.moveForward(dt);

etc ...
------

Now, I am no longer checking to see if the button was pressed in the Main Engine update and I am not sure how to properly attach Input Notifiers to events (or actions).

Now, I could simply do the same thing, not use InputNotifier and queue all the inputs that are made into InputManager. However, I would like to process through the InputManagers keys pressed as they were pressed. For example, with the above approach you check things in order of however your if's end us using it. I would like to process my inputs in the order they happen.

Share this post


Link to post
Share on other sites
szecs    2990
I was talking about nasty code, but you have to have a structure: decouple keyboard handling from the logic, if it's not event triggered.

I mean, if there's an event based action, put it into the keyboard handler, but if it's a regularly updated stuff: decouple.

I you want a certain time between the event handling (for example you waqnt to queue key-presses for a snake game), then I'd make my own keyboard queue, but only for the certain keys.


So in your example, I would use (non queued)
//update
float dt = gameTimer.fromLastUpdate()

if (MoveState.forward == TRUE )
camera.moveForward(dt);
...//event handling:
WM_KEYDOWN:
case 'W':
MoveState.forward = TRUE;
InputManager.buttonPressed = TRUE;
break;
...
// of course, use the OPP like setter/getters whatever


queued
...//event handling:

WM_KEYDOWN:
case 'W': case 'S': // and other keys you want buffed
CommandQueue.push(wParam);
break;

//update

float dt = gameTimer.fromLastUpdate()

...if ( time_passed_since_the_last_command_execution) // **sigh, I've just woken up )
ExecuteCommand(CommandQueue.pop());

Share this post


Link to post
Share on other sites
szecs    2990
Quote:
Original post by Hurp
Hmm, interesting ... so on your queue example you don't even use a listener at all do you?


I don't even know what listener is. And I have made many inputs of many kinds before.


@NumberXaero:
If the logic/behaviour requires this special kind of input handling. For example command queue, when the commands are executed in sequence, and there's a small time between these executions. Or the execution is executed only if a certain condition is true (so it cannot be executed the same time the event input occurs).
I hope that's clear.

Share this post


Link to post
Share on other sites
NumberXaero    2624
I dont know, seems over complicated. For me you have two cases, 1, you want things to execute at time of key press, ie feed some gui text box string. 2, you want continuous state of the current device, be it mouse or keyboard, and you check for a given state change each update, ie camera, you move as long as the key is down, and stop when the key goes up. Time of event is passed along in case 1, time of state change can be checked in case 2. Build state for each device and each key/button

- OnInput
-- Event received, up or down
-- Update device state (key/button)
-- Fire code that executes once, on up or down

// Device state is now current
- OnFrameUpdate
-- Pass state around
-- Fire code that executes continuously (character movement)

Im using something similar for what could become a very complex character movement that needs to know if key/button isDown, isUp, isFirstDown, isFirstUp, and the time involved with each, how long has it been down? how long has it been up? the time of first down? the time the key was released? and its worked well so far.

Share this post


Link to post
Share on other sites
Hurp    106
I just noticed that if are looking for WM_KEYDOWN and you press two buttons (such as "W" and "S") it will only send the input message for the last key you press, even if you are holding both. IS there any way to get the message for every button you are pressing?

Share this post


Link to post
Share on other sites
szecs    2990
Quote:
Original post by NumberXaero
I dont know, seems over complicated. For me you have two cases, 1, you want things to execute at time of key press, ie feed some gui text box string. 2, you want continuous state of the current device, be it mouse or keyboard, and you check for a given state change each update, ie camera, you move as long as the key is down, and stop when the key goes up. Time of event is passed along in case 1, time of state change can be checked in case 2. Build state for each device and each key/button


If you haven't used other methods it doesn't mean there's can't be a need for others. It's not about being complicated, it's about the BEHAVIOUR you want. I guess I wasn't clear in my previous post, I won't try to explain again.

Share this post


Link to post
Share on other sites
NumberXaero    2624
Quote:


Quote:

Original post by NumberXaero
I dont know, seems over complicated. For me you have two cases, 1, you want things to execute at time of key press, ie feed some gui text box string. 2, you want continuous state of the current device, be it mouse or keyboard, and you check for a given state change each update, ie camera, you move as long as the key is down, and stop when the key goes up. Time of event is passed along in case 1, time of state change can be checked in case 2. Build state for each device and each key/button


If you haven't used other methods it doesn't mean there's can't be a need for others. It's not about being complicated, it's about the BEHAVIOUR you want. I guess I wasn't clear in my previous post, I won't try to explain again.


Fine, your method is better, you win. It wasnt an attack, just a suggestion that queues to emulate what he was doing previously might be overkill and change things more then he wants.

Quote:
Buckeye: It's not very clear what problem you're trying to solve.


Quote:
Hurp:
Sorry about that, basically, I want to some how communicate to an object (my camera) that W has been pressed so it can preform a certain function.

The problem I am having is syncing up input with my update loop.

The way I use to have my update look when the input manager was not done through WndProc but rather DirectX was basically like this ...

------ Main Engine Update

float dt = gameTimer.fromLastUpdate()

if ( InputManager.buttonPressed(w) == true)
camera.moveForward(dt);

etc ...
------

Now, I am no longer checking to see if the button was pressed in the Main Engine update and I am not sure how to properly attach Input Notifiers to events (or actions).


So I suggested tracking the state of all buttons and keys and updating their state when input is received. After that you can fire event handlers that want to know about up/down events as they happen, and still check the state each frame as though you were checking directx during update.

Share this post


Link to post
Share on other sites
Hurp    106
My main issue I am having right now is that I want messages to pump when I am holding down two buttons, with WM_KEYDOWN it only sends one (the latest).

I was going to move to using WM_INPUT, however, this thread recommends that I do not. I do not fully understand why it is a bad idea to use WM_INPUT though. Could anyone share some insight on this?

Share this post


Link to post
Share on other sites
NumberXaero    2624
The thread never recommends against raw input. It just describes its behavior, which the poster thought was wrong.

One of the threads linked to within the thread you posted mentions exactly what I was talking about. The users problem is that hes basically tracking actions, key down/up when they happen, what you want to be tracking is state of each key, which you must maintain on your own. Use raw input to get direct up and down events, store these per key in an array of key states somewhere. When you need to you can check against this managed keyboard state and perform your logic based on whats up and down as combinations of keys if need be. When WM_INPUT is received, simply store the time it happened, when you get key up, check the time again, and determine how long it was down by using the time you stored on first down. There should be very little you cant do maintaining the entire state of each key and the time everything happened.

Storing your own state per key means you dont need explicit messages pumped from raw input, you just need to know that things go down, and they go up because you are essentially recording everything thing that is happening while the app runs.

Share this post


Link to post
Share on other sites
Hurp    106
Using WM_INPUT I am still unable to detect two keys simutanously being pressed. Here is what I will do

Press W and hold it :: Doing this will flood my Output window about it being pressed.

While still holding W, I press A and hold it. I am now holding W AND A. However, it is only detecting that A is being pressed. I can tell this by the fact that I am only seeing messages in my output about A being pressed.

Here is my code, does anyone know what I may be doing wrong?

WM_INPUT
{
UINT dwSize;

GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize,
sizeof(RAWINPUTHEADER));
LPBYTE lpb = new BYTE[dwSize];
if (lpb == NULL)
{
return 0;
}

if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize,
sizeof(RAWINPUTHEADER)) != dwSize )
OutputDebugString (TEXT("GetRawInputData doesn't return correct size !\n"));

RAWINPUT* raw = (RAWINPUT*)lpb;

char szTempOutput[512];
HRESULT hResult;

if(raw->data.keyboard.Message == 0x0100)
{
hResult = StringCchPrintf(szTempOutput, STRSAFE_MAX_CCH, TEXT(" Kbd: DOWN VK=%04x \n"),
raw->data.keyboard.VKey);

OutputDebugString(szTempOutput);
}
if(raw->data.keyboard.Message == 0x0101)
{
hResult = StringCchPrintf(szTempOutput, STRSAFE_MAX_CCH, TEXT(" Kbd: UP VK=%04x \n"),
raw->data.keyboard.VKey);

OutputDebugString(szTempOutput);
}
delete[] lpb;
return 0;

}
break;

Share this post


Link to post
Share on other sites
Hurp    106
It will be able to detect that W was released, here is my log

Kbd: DOWN VK=0057 // Press and hold W
Kbd: DOWN VK=0057
Kbd: DOWN VK=0057
Kbd: DOWN VK=0057
Kbd: DOWN VK=0041 // Press A and hold A, while still holding W
Kbd: DOWN VK=0041
Kbd: DOWN VK=0041
Kbd: UP VK=0057 // Release W, while still holding A
Kbd: DOWN VK=0041
Kbd: DOWN VK=0041
Kbd: UP VK=0041 // Release A

Share this post


Link to post
Share on other sites
NumberXaero    2624
Now store the state of that key, you know when it goes down, and you can assume its still down because you havent received an up message. Once youve stored it, it can be passed around to any place that needs to know if a keys down or up. This also allow for key combos to be checked for, because you have the state of all keys up to date at all times. Or you can use event handlers of some kind, and have the listeners, notifiers, observers, w/e, execute directly when down and up messages are received.

Share this post


Link to post
Share on other sites
Hurp    106
I could basically do the same thin with WM_KEYDOWN and WM_KEYUP, I don't see how using WM_INPUT adds an advantage ... how does it?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this