Handling multiple keys

Started by
7 comments, last by iMalc 16 years, 3 months ago
Hi! I'm using the WinAPI messages to handle input (keys & mouse) like this:
LRESULT MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	case WM_CLOSE:
		PostQuitMessage(0);
		return 0;

	case WM_SETCURSOR:
		SetCursor(NULL);
		Engine::GetInstance()->GetGraphicsModule()->GetDevice()->ShowCursor(FALSE);
		return 0;

	case WM_KEYDOWN:
		if(wParam == VK_ESCAPE)
		{
			PostQuitMessage(0);
			return 0;
		}

		Engine::GetInstance()->GetInputModule()->SetKey((int)wParam, true);

		return 0;

	case WM_KEYUP:
		Engine::GetInstance()->GetInputModule()->SetKey((int)wParam, false);

		return 0;

	case WM_MOUSEMOVE:
		Engine::GetInstance()->GetInputModule()->SetMousePosition(LOWORD(lParam), HIWORD(lParam));

		return 0;
	}

	return DefWindowProc(hwnd, msg, wParam, lParam);
}
I can't handle multiple keys with that. If I hold a key and after I hold another, the action stops... How can I handle multiple keys at the same time ? Thanks.
Advertisement
What you have there should work fine. Although because of the way keyboards work, some key combinations don't work (Although usually only with 3 or more keys).

What keys are you trying? And what "action" stops? What is this "action"?
Moving to General Programming.
Windows will handle multiple keys like that, yes. I think the troulbe lies in your engine's means of handling multiple keys. I'm guessing your code just sets the most recent key as a key being pressed. And when a new key is pressed it sets the new key and the engine no longer recognizes that the old key is still pressed.
Yes?
Keep in mind too that most keyboards are physically limited on the number of simultaneous key strokes that they can report. Many of the keys are aligned in rows and colums in order to reduce the number of connections needed inside the keyboard, where if the 'q' key is pressed row 'x' and column 'y' are signaled. Now, if you hit another key, say 'w', it might signal row 'x' and column 'y + 1'. In this case, even though the 'q' is still held down, it can tell that 'w' was pressed.

Now, if you have two keys held down that signal row 'x', column 'y' and row 'x + 1', column 'y + 1', pressing the key at row 'x', column 'y + 1' will go undetected. However, releasing the key at row 'x + 1' and column 'y + 1' might then allow the detection of the key at row 'x', column 'y + 1'. There's probably a convention used by keyboard manufacturers that spells this out.

I've heard of keyboards that are smarter than this that can detect all the keys individually, but most manufacturers obviously wanted the cheapest way to make them.

Please correct me if I'm wrong about this.
Quote:What keys are you trying? And what "action" stops? What is this "action"?


An action in my game like driving etc.

Quote:Windows will handle multiple keys like that, yes. I think the trouble lies in your engine's means of handling multiple keys. I'm guessing your code just sets the most recent key as a key being pressed. And when a new key is pressed it sets the new key and the engine no longer recognizes that the old key is still pressed.
Yes?


#pragma once#include "inc.h"class InputModule{private:	bool m_Keys[256];	HWND m_MainWindowHandle;	int m_Width;	int m_Height;	D3DXVECTOR2 m_RelativeMousePos;public:	InputModule(string name, int width, int height);	~InputModule();	void Create(string name, int width, int height);	HWND GetHandle();	void SetHandle(HWND hwnd);	int GetWidth();	int GetHeight();	bool GetKey(int key);	void SetKey(int key, bool value);	D3DXVECTOR2 GetMousePosition();	void SetMousePosition(int x, int y);};LRESULT MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);


#include "InputModule.h"#include "Logger.h"#include "Engine.h"LRESULT MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){	switch (msg)	{	case WM_DESTROY:		PostQuitMessage(0);		return 0;	case WM_CLOSE:		PostQuitMessage(0);		return 0;	case WM_SETCURSOR:		SetCursor(NULL);		Engine::GetInstance()->GetGraphicsModule()->GetDevice()->ShowCursor(FALSE);		return 0;	case WM_KEYDOWN:		if(wParam == VK_ESCAPE)		{			PostQuitMessage(0);			return 0;		}		Engine::GetInstance()->GetInputModule()->SetKey((int)wParam, true);		return 0;	case WM_KEYUP:		Engine::GetInstance()->GetInputModule()->SetKey((int)wParam, false);		return 0;	case WM_MOUSEMOVE:		Engine::GetInstance()->GetInputModule()->SetMousePosition(LOWORD(lParam), HIWORD(lParam));		return 0;	}	return DefWindowProc(hwnd, msg, wParam, lParam);}void InputModule::Create(string name, int width, int height){	m_Width = width;	m_Height = height;	WNDCLASSEX wc = {sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, (WNDPROC)MsgProc, 0L, 0L, GetModuleHandle(NULL), 		NULL, LoadCursor(NULL, IDC_CROSS), NULL, NULL, "D3DM", NULL};	if(!RegisterClassEx(&wc))	{		MSG_ERROR("Window registration failed");	}	MSG_SUCCESS("Windows registration succeeded");	m_MainWindowHandle = CreateWindow("D3DM", name.c_str(), WS_POPUP | WS_SYSMENU | WS_VISIBLE, 100, 100,		m_Width, m_Height, GetDesktopWindow(), NULL, wc.hInstance, NULL);	if (m_MainWindowHandle == NULL)	{		MSG_ERROR("Window creation failed");	}	MSG_SUCCESS("Windows creation succeeded");	if (!(ShowWindow(m_MainWindowHandle, SW_SHOW)))	{		MSG_ERROR("Couldn't show window");	}	if (!(UpdateWindow(m_MainWindowHandle)))	{		MSG_ERROR("Couldn't update window");	}}InputModule::InputModule(string name, int width, int height){	Create(name, width, height);	for (int i = 0; i < 256; i++)	{		m_Keys = false;	}}InputModule::~InputModule(){}D3DXVECTOR2 InputModule::GetMousePosition(){	return m_RelativeMousePos;}void InputModule::SetMousePosition(int x, int y){	m_RelativeMousePos.x = x;	m_RelativeMousePos.y = y;}bool InputModule::GetKey(int key){	return m_Keys[key];}void InputModule::SetKey(int key, bool value){	m_Keys[key] = value;}void InputModule::SetHandle(HWND hwnd){	m_MainWindowHandle = hwnd;}HWND InputModule::GetHandle(){	return m_MainWindowHandle;}int InputModule::GetWidth(){	return m_Width;}int InputModule::GetHeight(){	return m_Height;}


I'm using this code to detect if a key is pressed :

if (Engine::GetInstance()->GetInputModule()->GetKey(VK_UP))
{
// do something
}

I just want to hold 2 keys at the same time.

Thanks and sorry for the lateness.
I don't see any obvious reason for the problem from looking at that code. I do a similiar thing myself.
        case WM_SYSKEYDOWN:    if ((lParam & (1<<30)) == 0)                                   systemKeyDown((unsigned char)wParam); break;        case WM_SYSKEYUP:          systemKeyUp((unsigned char)wParam);   break;        case WM_KEYDOWN:       if ((lParam & (1<<30)) == 0)                                   keyDown((unsigned char)wParam);       break;        case WM_KEYUP:             keyUp((unsigned char)wParam);         break;        case WM_LBUTTONDOWN:       keyDown(VK_LBUTTON);    break;        case WM_LBUTTONUP:         keyUp(VK_LBUTTON);      break;        case WM_RBUTTONDOWN:       keyDown(VK_RBUTTON);    break;        case WM_RBUTTONUP:         keyUp(VK_RBUTTON);      break;        case WM_MBUTTONDOWN:       keyDown(VK_MBUTTON);    break;        case WM_MBUTTONUP:         keyUp(VK_MBUTTON);      break;        case WM_XBUTTONDOWN:       keyDown(GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? VK_XBUTTON1 : VK_XBUTTON2);    break;        case WM_XBUTTONUP:         keyUp(GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? VK_XBUTTON1 : VK_XBUTTON2);      break;
KeyDown and KeyUp are similiar to your SetKey and set the boolean in keyMap, though they do a little more work to do things like detect left-shift vs right-shift.
Then I use the following to handle key presses upon each tick, where each key can be bound to a function returning a bool as to whether to accept it must be released and pressed again to be noticed again:
void HandleKeys() {	for (KeyList::iterator it = pressedKeys.begin(); it != pressedKeys.end();) {		if (keyMap[*it] && keyBinds[*it]())			++it;		else			it = pressedKeys.erase(it);	}}
keyBinds are setup by parsing a Config file, and looking up the key and function names in tables, to set entries in keyBinds.
So as you can see, doing the same kind of thing works for me.

Can you confirm that your keyboard does detect two keys down at the same time in other programs?
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
Quote:Can you confirm that your keyboard does detect two keys down at the same time in other programs?


Yes.

In my program too, I can hold 2 keys at the same time. The problem is that I can hold a key (like z to move forward) but after if I hold a second key (a to move left), the movement is replaced by the last key hold or the movement stops
Are you sure you don't have code clearing m_Keys each frame?
Doing that could cause the behaviour you see as the first key you press down would auto-repeat until you pressed another key.

Try adding code to log the states of m_Keys[VK_UP] and m_Keys[VK_LEFT] for example, and the calls made to SetKey, logging the values passed in. Then run the code and press both of those keys.
I think you'll find that something else is clearing the m_Keys array. If not, please let us know what you find.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms

This topic is closed to new replies.

Advertisement