PDA

View Full Version : How can I capture keyboard input... ?



pew1234
10-11-2002, 03:33 AM
Hi All,

I am writing a little game in Delphi and the main screen of the game is a standard TImage. Actually I'm using a custom font to draw the image (but that's another story). What I need to know is what is the best way for me to capture keyboard input ???

Currently I am using a multitabbed form with the TImage on one of the tabs (and settings on another tab). I am also using a TEdit box to provide me with keypressed events (for testing)... but this seems like a clumsy way for me to do it. Is there another way that I can get keypressed events on the form without needing to place a Tedit or similar on the form ??? (as I don't really want to have a tedit on the form... I am just using it to give me the keypressed events for testing).

Sample code (fragments) would be appreciated.

Regards,
--- Peter W.
Canberra, Australia

Xorcist
10-11-2002, 06:49 AM
I guess you could do it the quick and easy way and use virtual key codes and keypress events, but it might be wiser to capture the keyboard state all at once, using the GetKeyboardState function, to find out what keys are depressed (you'll have to call this in your main game loop so the state gets continually updated).


procedure GetInput();
var
KeyState: TKeyboardState;
begin
if (GetKeyboardState(KeyState) <> 0) then begin
if ((KeyState[vk_Up] and $80) <> 0) then begin
{Put code to handle up arrow key here}
end;
if ((KeyState[vk_Down] and $80) <> 0) then begin
{Put code to handle down arrow key here}
end;
if ((KeyState[vk_Right] and $80) <> 0) then begin
{Put code to handle right arrow key here}
end;
if ((KeyState[vk_Left] and $80) <> 0) then begin
{Put code to handle left arrow key here}
end;
end else begin
{GetKeyboardState failed, handle it if necessary}
end;
end;



Here (http://216.26.168.92/vbapi/ref/other/virtualkeycodes.html) is a list of virtual key codes to help you out. Note: ($80 is hex for 128), Updated the code slightly.

pew1234
10-11-2002, 08:23 AM
Hi,

Thanks for the code. I will give it a try (later).

Bye.

Xorcist
10-11-2002, 09:20 AM
Just in case your not sure what's happening, and you'd like a little insight, here is a quick overview:

GetKeyboardState returns a nonzero value if successful, otherwise it returns zero (which is why we only check KeyState if the return value of GetKeyboardState was nonzero). If GetKeyboardState does succeed it will fill the KeyState array with 256 bytes, each byte representing one of the keys on the keyboard. Of those bytes, for the majority of keys, only the high order bit determines whether the key is pressed or not (though toggle keys like CAPSLOCK and NUMLOCK work differently). Which is why we AND the value with 128 (or 10000000 in binary). If the result of that AND is not zero that means the high order bit must be one, and thus the key is being pressed.

:)

pew1234
10-11-2002, 08:51 PM
Hi,

Is there a better way for me to do this:

PEW


procedure GetInput();
var
KeyState: TKeyboardState;
inputkey : integer;
begin
inputkey := 0;
if (GetKeyboardState(KeyState) <> 0) then
begin
// I only want to capture keys VK_NUMPAD1..VK_NUMPAD9
// and return inputkey = 1..9 respectively

if ((KeyState[VK_NUMPAD1] and $80) <> 0) then
inputkey := 1;
if ((KeyState[VK_NUMPAD2] and $80) <> 0) then
inputkey := 2;
if ((KeyState[VK_NUMPAD3] and $80) <> 0) then
inputkey := 3;
if ((KeyState[VK_NUMPAD4] and $80) <> 0) then
inputkey := 4;
if ((KeyState[VK_NUMPAD5] and $80) <> 0) then
inputkey := 5;
if ((KeyState[VK_NUMPAD6] and $80) <> 0) then
inputkey := 6;
if ((KeyState[VK_NUMPAD7] and $80) <> 0) then
inputkey := 7;
if ((KeyState[VK_NUMPAD8] and $80) <> 0) then
inputkey := 8;
if ((KeyState[VK_NUMPAD9] and $80) <> 0) then
inputkey := 9;
end
else
; // don't care
{GetKeyboardState failed, handle it if necessary}
end;

Xorcist
10-11-2002, 09:48 PM
Well, considering inputkey is a local variable, it'll only have relevance in this procedure. If this data is required elsewhere you'll need to either change the procedure to a function that returns an integer and make sure you set result := inputkey, or make inputkey a global variable.

Also keep in mind GetKeyboardState returns the state of the keyboard at the time the function was called (this includes all keys that are being pressed). So if VK_NUMPAD1 and VK_NUMPAD9 are simultaniously being pressed, the code above will set inputkey to be 9. Keys which you want to give a higher presidence to should always be lower on the list.

If you wish to avoid this entirely, and don't plan to actually handle the keys in this procedure, but rather are just gathering data and will handle these keys at a later time you might want to make inputkey a word rather than an integer and map each key to a bit.

ex.
inputkey: word;

0000000000000001 = NUMPAD1 = 1
0000000000000010 = NUMPAD2 = 2
0000000000000100 = NUMPAD3 = 4
0000000000001000 = NUMPAD4 = 8
0000000000010000 = NUMPAD5 = 16
0000000000100000 = NUMPAD6 = 32
0000000001000000 = NUMPAD7 = 64
0000000010000000 = NUMPAD8 = 128
0000000100000000 = NUMPAD9 = 256

then when you set each inputkey you just OR the proper value so if VK_NUMPAD1 and VK_NUMPAD9 being simultaniously being pressed:

inputkey := inputkey OR 1;
inputkey := inputkey OR 256;

and inputkey would be equal to 0000000100000001 (257 decimal). Then elsewhere in your code you could use the same method of ANDing we used originally (using these new inputkey specific values, not virutal keycodes), to findout what keys of inputkey are being pressed.

if ((inputkey and "decimal value goes here") <> 0) then begin
end;

Hope that all makes sense. If not just let me know, I'll try to clear it up a little.

pew1234
10-11-2002, 10:11 PM
Hi,

I meant to say that inputkey is a var parameter. I was writing it off the cuff without Delphi :-) !!

In the game that I am writing I am only interested in a single VK_NUMPAD 1..9 keys being pressed. Multiple key presses don't worry me, but technically they are not valid.


procedure GetInput(var inputkey : integer);
var
KeyState: TKeyboardState;
begin
inputkey := 0;
if (GetKeyboardState(KeyState) <> 0) then
begin
// I only want to capture keys VK_NUMPAD1..VK_NUMPAD9
// and return inputkey = 1..9 respectively

if ((KeyState[VK_NUMPAD1] and $80) <> 0) then
inputkey := 1;
if ((KeyState[VK_NUMPAD2] and $80) <> 0) then
inputkey := 2;
if ((KeyState[VK_NUMPAD3] and $80) <> 0) then
inputkey := 3;
if ((KeyState[VK_NUMPAD4] and $80) <> 0) then
inputkey := 4;
if ((KeyState[VK_NUMPAD5] and $80) <> 0) then
inputkey := 5;
if ((KeyState[VK_NUMPAD6] and $80) <> 0) then
inputkey := 6;
if ((KeyState[VK_NUMPAD7] and $80) <> 0) then
inputkey := 7;
if ((KeyState[VK_NUMPAD8] and $80) <> 0) then
inputkey := 8;
if ((KeyState[VK_NUMPAD9] and $80) <> 0) then
inputkey := 9;
end
else
; // don't care
{GetKeyboardState failed, handle it if necessary}
end;


I guess that I could rewritten the if statements to use elses:

...
if ((KeyState[VK_NUMPAD1] and $80) <> 0) then
inputkey := 1
else if ((KeyState[VK_NUMPAD2] and $80) <> 0) then
inputkey := 2
else if ...

Xorcist
10-11-2002, 10:37 PM
If multiple keypressed aren't a worry, then I'd say stick with the original setup you had. No need to conditionalize the whole list if only one key is going to be pressed at any one time. And if you need any key to have a higher presidence than one of the others just put it farther down on the list.

pew1234
12-11-2002, 05:10 AM
Hi,

I got a different answer (from delphi-programming@yahoogroups.com) which I think is more like what I had in mind:

>>>
Hi ...
You can use Tform.OnKeyDown to capture the inputs,
This is don't need an edit box to detect it :)
And also set the KeyPreview := True in the Form properties

Hope it helps ...
Regards,
Billy Lapono
<<<

Regards,
--- Peter W.
Canberra, Australia

TheLion
12-11-2002, 07:49 AM
Sure OnKeyDown works just fine, BUT if you want to be able to say move your spacecraft and fire at the same time it probably won't work in that case you'll have to use either GetAsyncKeyState or GetKeyBoardState to be able to do that! :)

I used OnKeyDown in the beginning, but I ran into it's limitations and switched to GetAsyncKeyDown and used it ever since, but it really depends on what type of game you are writing and what sort of input is required! :)

Xorcist
12-11-2002, 08:47 PM
Actually that's the first thing I mentioned:


I guess you could do it the quick and easy way and use virtual key codes and keypress events, but...

However, like TheLion stated, and why I said it was wiser to capture the entire keyboard state at once, was the fact that the keypress events are bound to a form and generally do not handle multiple simultanious keypesses all that well. Capturing the keyboard state will work even if no Tform is present (done from scratch, or as a console app) and generally allows you greater control over your game input.

Alimonster
12-11-2002, 08:53 PM
OnKeyDown can lead to very inconsistent movement (speeding up/slowing down depending on events passed in, different keyboard repeat rates, etc.). Not recommended, dude. Use GetKeyState, GetAsyncKeyState or GetKeyboardState instead.