Sign in to follow this  
Sammie22

Raw Input and Key Combinations

Recommended Posts

Trying to handle key combinations with Raw Input API and having some trouble. For example, in a typical FPS, you can press 'W' and 'D' and move forward and to the right. Releasing 'D' would still leave you moving forward... etc. What I have done is this:
                // keyboard
                if( raw->header.dwType == RIM_TYPEKEYBOARD )
                {
                    // get key value from the keyboard member
                    USHORT key_code = raw->data.keyboard.VKey;

                    // key down
                    if( raw->data.keyboard.Flags == RI_KEY_MAKE )
                    {
                        if( key_code == 'W' )
                        {
                            g_Camera->MoveForward( g_Camera_Movement_Speed );
                        }
                        if( key_code == 'S' )
                        {
                            g_Camera->MoveForward( -g_Camera_Movement_Speed );
                        }
                        if( key_code == 'A' )
                        {
                            g_Camera->MoveRight( -g_Camera_Movement_Speed );
                        }
                        if( key_code == 'D' )
                        {
                            g_Camera->MoveRight( g_Camera_Movement_Speed );
                        }
                    }
This only captures the last key pressed. Does anyone know of any samples which handle key combinations? Or can anyone help on getting this to work? Thanks, Brad

Share this post


Link to post
Share on other sites
A simple way to approach this is to give the camera a vector which can represent multiple key inputs. Something like:
Camera::Move(Vector2 v);

Then in your input code you can accumulate different buttons being pressed into a vector. For example if 'W' and 'A' are pressed your vector would be
Vector2(-g_Camera_Movement_Speed, g_Camera_Movement_Speed)
which will move the camera forward and to the left.
In your 'if' statements, instead of directly moving the camera you would adjust the vector. ex, 'A' again:
camVec.x -= g_Camera_Movement_Speed;

At some point in your main loop you would call Camera::Move using camVec.


There are other considerations based on how often you receive input messages and whether or not there are KEYDOWN/KEYUP pairs.

Share this post


Link to post
Share on other sites
Ok I will give that approach a try. But I am still perplexed as to why the last key pressed is the only key registered? If I hold down 'W', then press 'D', will 'W' still report?

I also tried doing this like (psuedo code since I scraped it already):

if key down
{
start moving in proper direction
}

if key up
{
stop moving in that direction
}


I thought this might handle it as the key up message might be sent at a different time for each key. But that did not seem to work either.

Thanks for the help,
Brad

ps. What is the code tag for posting?

pss. Here is a more complete cod listing for what I have done too:

Set it up
=========
//------------------------------------
// Raw Input (keyboard and mouse)
//------------------------------------
// keyboard
g_RID[0].usUsagePage = 1;
g_RID[0].usUsage = 6;
g_RID[0].dwFlags = 0;
g_RID[0].hwndTarget = NULL;
// mouse
g_RID[1].usUsagePage = 1;
g_RID[1].usUsage = 2;
g_RID[1].dwFlags = 0;
g_RID[1].hwndTarget = NULL;
// register
RegisterRawInputDevices( g_RID, 2, sizeof( RAWINPUTDEVICE ) );

Messages
=========
case WM_INPUT:
{
// determine size of buffer
UINT buffer_size;
GetRawInputData( ( HRAWINPUT ) lParam, RID_INPUT, NULL, &buffer_size, sizeof( RAWINPUTHEADER ) );

// create buffer with correct size
BYTE* buffer = new BYTE[ buffer_size ];

// get the data
GetRawInputData( ( HRAWINPUT ) lParam, RID_INPUT, ( LPVOID ) buffer, &buffer_size, sizeof( RAWINPUTHEADER ) );

// process data
RAWINPUT* raw = ( RAWINPUT* ) buffer;
...

The rest is in the code I pasted in the first post.

[Edited by - Sammie22 on March 4, 2010 10:43:06 PM]

Share this post


Link to post
Share on other sites
Quote:
But I am still perplexed as to why the last key pressed is the only key registered?
You are thinking about looking at the state of the keys. That's not the information being provided though. The information is really the last action that happened. So if you press W, what's the last action? Pressing W. Then while holding W, what's the last action? Pressing W. Then while holding W, you press D. What's the last action? Pressing D. Then while holding W and D, you release W. What's the last action? Releasing W.

What you are getting and what you want are different. You are getting actions. You want states or list of keys in a certain state. You want to either know the states of all keys on the keyboard, or a list of keys in the down state. You need to do additional work on your end to track this information.

Share this post


Link to post
Share on other sites
Ok thanks.. that's kinda what I tried in a different attempt (the psuedo code), but I must have implemented it wrong. I will try again and see if I can get it to work.

Edit (just tested your post):

When I press 'W' I move forard until another kb action is taken. I then press 'D' and move right until another action. If I release 'D' I stop. This fits your description perfectly.

But, when I do the same, and instead release 'W' I keep moving right. But that is not the last action taken?



Edit 2:

I ran some logging on the key-down / key-up events, and the associated key-pressed / key-released events. The last key-down event is reported continuously, until it is released. Key-release events from keys other than the last-key-pressed are sent once while the last-key-pressed remains down. When the last-key-pressed is released, no other kb events are sent except key-releases. So I guess raw input always reports the last-key-pressed until it is released, and key-releases are only reported once regardless of the key-press state?



Appreciate the help!

[Edited by - Sammie22 on March 5, 2010 2:07:51 AM]

Share this post


Link to post
Share on other sites
Quote:
The last key-down event is reported continuously, until it is released. Key-release events from keys other than the last-key-pressed are sent once while the last-key-pressed remains down.

I haven't used the rawinput api but it sounds like you are guaranteed (per key) at least one keydown message and exactly one release message.

As oler1s said, to get the functionality you want you'll need to keep track of which keys are pressed. To reiterate: you shouldn't be using the MsgProc to directly move the camera because you are only given the last key pressed. Just record when keys are pressed and when they are released. Somewhere else in your main loop you can apply the key state to the camera. Tracking key states gives you information about all keys whereas depending on the current rawinput poll only gives you information about one key.

Share this post


Link to post
Share on other sites
Ya I got that part working the way I want it now thanks to oler1s pointing out I am receiving an action, not a state. It is inconsistent to what was oler1s described, from my tests, which I was curious about. I'll post the code which might help others that may have the same question (in addition to above code):


//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
static bool w_down = false;
static bool s_down = false;
static bool a_down = false;
static bool d_down = false;



// keyboard
if( raw->header.dwType == RIM_TYPEKEYBOARD )
{
// get key value from the keyboard member
USHORT key_code = raw->data.keyboard.VKey;

if( g_LoggingEnabled )
{
g_Logger->Log( " Keyboard: " );
g_Logger->Log( IntToString( raw->data.keyboard.VKey ) );
g_Logger->Log( "\t" );
g_Logger->Log( " Flags: " );
g_Logger->Log( IntToString( raw->data.keyboard.Flags ) );
g_Logger->Log( "\n" );
}

// key down
if( raw->data.keyboard.Flags == RI_KEY_MAKE )
{
if( key_code == 'W' ) w_down = true;
if( key_code == 'S' ) s_down = true;
if( key_code == 'A' ) a_down = true;
if( key_code == 'D' ) d_down = true;
}

// key up
if( raw->data.keyboard.Flags == RI_KEY_BREAK )
{
if( key_code == 'W' ) w_down = false;
if( key_code == 'S' ) s_down = false;
if( key_code == 'A' ) a_down = false;
if( key_code == 'D' ) d_down = false;
if( key_code == VK_SPACE ) jump = true;
}


Then in some other function / method:


//-----------------------------------------------------------------------------
// Name: KeyboardMovement()
// Desc: Applies movement to camera based on raw keyboard input
//-----------------------------------------------------------------------------
VOID KeyboardMovement()
{
if( w_down )
{
g_Camera->MoveForward( g_Camera_Movement_Speed );
}
if( s_down )
{
g_Camera->MoveForward( -g_Camera_Movement_Speed );
}
if( a_down )
{
g_Camera->MoveRight( -g_Camera_Movement_Speed );
}
if( d_down )
{
g_Camera->MoveRight( g_Camera_Movement_Speed );
}
}


According to toymaker.info, probably best not to use a dynamically allocated array to store on each input message. And don't forget to delete[] it if you do (I did, til I caught it).

Thanks for the help. I got my little prototype working and now I can move on to the next piece :)

ps. still don't know the proper tags to post code. using [ code ] is that correct ?

[Edited by - Sammie22 on March 6, 2010 4:05:37 AM]

Share this post


Link to post
Share on other sites
Quote:
ps. still don't know the proper tags to post code. using [ code ] is that correct ?
To get a scroll-able source box, use [source].

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