Clean Keyboard Input Processing

Started by
3 comments, last by FirestormFenix 20 years, 8 months ago
I'm using DirectInput to process keyboard input, and basically for my keyboard class, I have an array that holds the states of each key, and two functions, IsKeyDown() and IsKeyUp(), that test the array for a specific key (passed in as an argument) and check whether it is being pressed or not. Also, I have a function, Update(), that is called each frame and an updates the array that holds the states of each key. My main problem is that in my game, it seems as if input is being read "too fast." For instance, I have a menu on the title screen with 2 items, and a cursor that can be moved up and down to choose an item, and if the cursor is on the second item of the list and down is pressed, it moves back to the first item, and if the cursor is on the first item of the list and down is pressed, it moves back to the second item. If you push the down arrow once, even for a short period of time, it seems as if the program is reading multiple key presses (the cursor will move to the second item, then the first, then the second again). What I suspect is that the input is processed faster than it takes the user to release the key, so many frames are going by and asserting the key as being down. This makes any kind of movement of cursors and other objects very sloppy, since they tend to move past where you'd expect them to move. Is there some way to delay this? [edited by - FirestormFenix on July 26, 2003 4:55:40 PM]
_________________~Firestorm Fenix~
Advertisement
Instead of calling the update() function based on frames, could you do it based on time (milli-seconds)? That way you can adjust it to play and input smoothly no matter what machine is running the game. If you have ever tried old DOS games that were programmed assuming the processor clock speed would be constant, on a newer machine they have the same problem: they run so fast that they are unplayable.
You''re right about the cause of the problem, I had one just like it.
I solved it like so:

First make the array of keys an array of ints:

int keys[NUM_KEYS];

Then in the Update function, if a key is pressed increment the integer:

UpdateKeys()
{
for(int r = 0; r < NUM_KEYS; r++)
if(IsKeyDown(r))
keys[r]++;
else
keys[r] = 0;
}

Finally when your reading the keys do something like this:

ReadKeys()
{
if(keys[UP_ARROW] % 4 == 1)
//Go up...
}

The % 4 will keep the key from firing every frame. You can make the number bigger if you want the cursor to move slower, or smaller if you want it to move quickly.

Also, when you don''t want the user to have the ability to hold a key down you could just say:

if(keys[WHATEVER_KEY] == 1)

This way, the cursor or whatever would only move on 1 frame (the user has to keep pressing the key over and over).

Well, that''s the way I did it anyway.
Hope that was helpful (and understandable).
If you only want to respond to a keypress once, and then only respond if the player releases and presses the key again, you can do something like this:

//// Declared in my Direct Input Class header// BYTE KeyStates[256]; // Array of current key statesBYTE OldKeyStates[256]; // Array of previous key states

//  In my ''Update'' function I do this every frame//// Store the last KeyStates array in OldKeyStates and clear//  the KeyStates buffermemcpy(&OldKeyStates, &KeyStates, sizeof(KeyStates));ZeroMemory(&KeyStates, sizeof(KeyStates));// Read the keyboardHR = DI_Keyboard->GetDeviceState(sizeof(KeyStates), &KeyStates);

//// These two functions are the standard KeyDown/Up tests//bool KeyDown(BYTE Keycode){  return ((KeyStates[Keycode] & 0x80) == 0x80);}bool KeyUp(BYTE Keycode){  return ((KeyStates[Keycode] & 0x80) == 0);}//// These functions only return true if the key has just been// pressed/released and return false in subsequent frames// until the key is released/pressed again//bool KeyJustDown(BYTE Keycode){  // if previous KeyStates array shows key up and current shows key down, then the key has just been pressed  return (((KeyStates[Keycode]  & 0x80) == 0x80) &&((OldKeyStates[Keycode]  & 0x80) == 0));}bool KeyJustUp(BYTE Keycode){  // if previous KeyStates array shows key down and current shows key up, then the key has just been released  return (((KeyStates[Keycode]  & 0x80) == 0) && ((OldKeyStates[Keycode]  & 0x80) == 0x80));}


If you want the user to be able to hold down a key, but for the cursor or menu item to move slowly, I''d suggest using a timer and storing the time when the key is first pressed. In subsequent frames test not only whether the key is down, but also whether current_time - time_the_key_was_first_pressed > whatever_delay_you_want_between_responses. That will make it frame independent so your menu, cursor, or whatever will respond at the same speed on all PCs.

Hope that helps.

Thanks a lot for all of your input guys, it was really beneficial and has been great for me since I''m in the process of learning.

Hawflakes: I was thinking about delaying the Update() function but wouldn''t that mean that some keypresses would get missed? In regards to your mention of the DOS games, heh, that is a very humorous problem. Some semi-modern games still have that problem. Ever play the Magic: The Gathering game? It''s quite humorous.

Jeboro: I tried out your method and it worked a little bit better than what I was doing (I was using a bool array instead of an int array to store key presses), but multiple key presses were still slipping in even after I increased the constant. Plus, I think your method is machine dependent since the value of that constant you''re modulo-ing would change on faster or slower machines.

MisterMoot: Your method worked like a dream. I got the responses I was looking for when I tested it out. Thanks a ton!



_________________
~Firestorm Fenix~
_________________~Firestorm Fenix~

This topic is closed to new replies.

Advertisement