Thread: Check key state once per frame

  1. #1
    Math wizard
    Join Date
    Dec 2006
    Location
    USA
    Posts
    582

    Check key state once per frame

    By using GetAsyncKeyState, I can get objects to move, however, this function is based solely on what the keyboard delay and repeat rates are as set in windows and is bad for games and interactive animations. In my case, it's 250 milliseconds delay and 30 repeats per second afterwards. I need it so that the state of the key is checked each frame causing no delays at all. This is my code (for the relevant area near the bottom) if it helps:

    Code:
    	while(GameRunning == 1) // while the game is running
    	{
    		PeekMessage(&msg,hwnd,NULL,NULL,PM_REMOVE);
    		if (msg.message == WM_QUIT) // check for a quit message
    		{
    			GameRunning = 0; // if found, quit the game
    		}
    		
    		else
    		{
    			AdvanceClock(); // advance the clock forward one frame
    			
    			if (FramesDrawn < 4000) // test only
    			{
    				ProcessFrameData(); // obtains the image for displaying
    			}
    
    			// After this, we display the finished image.
    			if (FramesDrawn < 5000) // again, test only
    			{
    				// display the output obtained in the previous function call
    				DrawDibDraw(ImageHandle, HDCScreen, 0, 0, MainDisplayHead.biWidth, MainDisplayHead.biHeight, BMPHeadPointer, BMPImagePointer, 0, 0, MainDisplayHead.biWidth, MainDisplayHead.biHeight, NULL);
    				FramesDrawn++;
    			}
    			
    			if (FramesDrawn < 4000) // yep, gotta love tests
    			{
    				// The state of the keys should be checked every time a frame is processed
    				
    				if (GetAsyncKeyState(KeyUp) == KeyOn) // KeyUp is 0x26 and KeyOn is defined as -32767 (from an experiment)
    				{
    					TestImage.y--; // moves test image up
    				}
    
    				if (GetAsyncKeyState(KeyDown) == KeyOn) // KeyDown is 0x28
    				{
    					TestImage.y++; // moves test image down
    				}
    			}
    			
    			/* Translate and dispatch to event queue */
    			TranslateMessage(&msg);
    			DispatchMessage(&msg);
    		}
    	}
    Edit: It won't let me color the text. It's the bottom area in question.
    Last edited by ulillillia; 04-08-2007 at 02:19 PM. Reason: Forum software won't let me color the text

  2. #2
    Registered User OnionKnight's Avatar
    Join Date
    Jan 2005
    Posts
    555
    Aren't you thinking of the WM_KEYDOWN message? GetAsyncKeyState returns immediately.

    Also, you might want to use a virtual key such as VK_UP instead of KeyUp and check whether the high bit is set in the return value instead of comparing it to KeyOn, but that doesn't really solve your problems.
    http://msdn.microsoft.com/library/de...nckeystate.asp

  3. #3
    Math wizard
    Join Date
    Dec 2006
    Location
    USA
    Posts
    582
    This doesn't seem to explain it well. Is it a function call (that is, having it like "WM_KEYDOWN(0x26, 0x00002600);"), or something else (for the up arrow key and I don't fully understand the last parameter? The braces are missing. What about multiple keys being pressed such as the control (0x11), shift (0x10), and left arrow key (0x25)? Sometimes, multiple keys need to be checked. In my case above, I could just use if statements and if true, have the simple movement as I've given in my small sample.

    By the way, in a self-made header that includes the keyboard input values, I've got defines like this:

    #define KeyUp 0x26
    #define KeyDown 0x28

    This way, it's easier to use since the 0 through 9 and alphabet keys don't have VK_# for them. I just thought I'd let you know about that.

  4. #4
    Registered User OnionKnight's Avatar
    Join Date
    Jan 2005
    Posts
    555
    It's a define using some random integer.
    Your window procedure is called whenever your window recieves a message and the message WM_KEYDOWN would be put in the Msg variable for the function whose head looks like below.
    LRESULT CALLBACK WndProc (HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
    Quote Originally Posted by ulillillia View Post
    This way, it's easier to use since the 0 through 9 and alphabet keys don't have VK_# for them.
    They don't have any defines but they can still handled with virtual keys and it's as simple as using a character literal as the argument like '0', '9' or 'W'. Note that it only applies for capitalized letters, 'z' wouldn't work for example.

  5. #5
    Math wizard
    Join Date
    Dec 2006
    Location
    USA
    Posts
    582
    Ah, so do I have this set up correctly?

    Code:
    short KeyPressed; // wParam appears to be 16-bit
    
    ...
    LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	...
    		case WM_KEYDOWN:
    			KeyPressed=wParam;
    			return 0;
    			break;
    	...
    }
    
    int APIENTRY WinMain(HINSTANCE hInstance,
                         HINSTANCE hPrevInstance,
                         LPSTR     lpCmdLine,
                         int       nCmdShow)
    {
    	...
    	while(GameRunning == 1) // while the game is running
    	{
    		PeekMessage(&msg,hwnd,NULL,NULL,PM_REMOVE);
    		...
    				if (KeyPressed == KeyUp) // KeyUp is 0x26
    				{
    					TestImage.y--; // moves test image up
    				}
    		...
    I'm still fairly new with this stuff....

  6. #6
    Registered User OnionKnight's Avatar
    Join Date
    Jan 2005
    Posts
    555
    Yes, if the case WM_KEYDOWN belongs to a switch (Msg). Also note that the wParam for WM_KEYDOWN is in virtual key format unless that's what you wanted. Since you TranslateMessage() a WM_CHAR message will also be sent after the WM_KEYDOWN which will give you the ASCII-value of a key pressed.

  7. #7
    Math wizard
    Join Date
    Dec 2006
    Location
    USA
    Posts
    582
    That's all I'm mainly after, telling if a key is pressed or not. For example, in my main function, I have a loop (as seen in my first message). For example, if I had the "T" key pressed and it called a function to change the time scale (run at 60 fps, but have the appearance of 240 by quadrupling the time passed per frame), I could have the time-scaling function called (just once of course (and I already know how to limit it to once)) and the user would be able to set the time scaling value as intended. If the user pressed the left arrow key, the character would move left and the background would move right according to the background-scaling value (aka distance).

    Just wondering, but what differences, if any, are there in telling if there are multiple keys pressed? For example, if a user holds the control key to float in the air, the user could also use the arrow keys to move around in the air, but once the control key is let go, gravity takes over and the character falls. This could involve as many as 3 checks, but even 5 or 6 may be needed for the high end (the four arrow keys and the control/shift keys).

  8. #8
    Captain - Lover of the C
    Join Date
    May 2005
    Posts
    341
    GetAsyncKeyState is nothing short of amazing when you know how to use it. As you have already figured out, it doesn't return a simple 1 or 0 to tell whether or not the key is pressed. If the key is simply pressed, the most significant bit is set. If the key was pressed since you last called GetAsyncKeyState, the least significant bit is set. If you just want to check if the key is currently pressed, then you need to check the most significant bit. How? It's actually pretty simple. First mask it with 0x8000 to single out the most significant bit. If the value isn't returned by this is zero then the most significant bit was never set and the key is up. If the value isn't zero then the most significant bit was set and the key is down. Here are a couple macros to help you out:
    Code:
    #define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
    #define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
    You would then use the macros like this:
    Code:
    if (KEY_DOWN(VK_ESCAPE))
        // Exit the program :-)
    Last edited by Brad0407; 04-09-2007 at 08:12 AM. Reason: Couldn't type in a straight line even if I wanted to
    Don't quote me on that... ...seriously

  9. #9
    Registered User OnionKnight's Avatar
    Join Date
    Jan 2005
    Posts
    555
    Quote Originally Posted by ulillillia View Post
    Just wondering, but what differences, if any, are there in telling if there are multiple keys pressed? For example, if a user holds the control key to float in the air, the user could also use the arrow keys to move around in the air, but once the control key is let go, gravity takes over and the character falls. This could involve as many as 3 checks, but even 5 or 6 may be needed for the high end (the four arrow keys and the control/shift keys).
    It all depends on how you implement it. If you were to do if-statements for each key you'd probably have as many statements as you have keys however if you took another approach such as translating the keys into a direction you could make them fewer like
    Code:
    int player_posx = 23;
    int player_speed = 3;
    
    bool leftkey_down = (GetAsyncKeyState(VK_LEFT) & 0x8000) != 0;
    bool rightkey_down = (GetAsyncKeyState(VK_RIGHT) &  0x8000) != 0;
    int direction = rightkey_down - leftkey_down;
    
    /* leftkey_down and rightkey_down has the values 1 or 0 so
    left right dir
     0    0     0
     1    0    -1
     0    1     1
     1    1     0
    */
    
    player_posx += direction*player_speed;
    
    /* If the player presses left, the x-value decreases by speed and if right is pressed it increases */

  10. #10
    Math wizard
    Join Date
    Dec 2006
    Location
    USA
    Posts
    582
    Thanks for the details, but you're going back to the GetAsyncKeyState function again. GetAsyncKeyState won't work because of the keyboard delay and repeat rate being bad for games. The state needs to be checked once per frame and I've been told I need to use the WM_KEYDOWN event thing. Looking at the design, this only seems to work for one key. How would it work for multiple keys (such as control+shift+left) being individually set? Let's say someone is pressing the control key to float in the air. That alone is okay, but what about movement? That's what the arrow keys are for and if the user presses the up and right arrow keys, the character would move upwards slowly and burst rapidly (accelerate (to a point)) to the right. GetAsyncKeyState will only work to up to 30 fps, but this varies depending on preferences set in the Windows control panel. It needs to be checked every frame (60 fps) without any delays.

  11. #11
    Captain - Lover of the C
    Join Date
    May 2005
    Posts
    341
    Quote Originally Posted by ulillillia View Post
    Thanks for the details, but you're going back to the GetAsyncKeyState function again. GetAsyncKeyState won't work because of the keyboard delay and repeat rate being bad for games. The state needs to be checked once per frame and I've been told I need to use the WM_KEYDOWN event thing. Looking at the design, this only seems to work for one key. How would it work for multiple keys (such as control+shift+left) being individually set? Let's say someone is pressing the control key to float in the air. That alone is okay, but what about movement? That's what the arrow keys are for and if the user presses the up and right arrow keys, the character would move upwards slowly and burst rapidly (accelerate (to a point)) to the right. GetAsyncKeyState will only work to up to 30 fps, but this varies depending on preferences set in the Windows control panel. It needs to be checked every frame (60 fps) without any delays.
    I don't think there is a single fact in that post. GetAsyncKeyState returns the State of the Key. Hence the name. WM_CHAR and WM_KEYDOWN would fit the above description of delay and repeat rates. Perhaps you've gotten GetAsyncKeyState confused with them. Let me clear that up.
    1. If the key is down, GetAsyncKeyState will tell you that the key is down.
    2. If the key is up, GetAsyncKeyState will tell you that the key is up.
    3. GetAsyncKeyState doesn't give a care about repeat rates
    4. GetAsyncKeyState definitely doesn't have a delay.
    5. GetAsyncKeyState doesn't care how many or which keys are pressed, it cares only about the key in question.

    You seem very convinced that you are right so I made a lovely test of GetAsyncKeyState for you to play with. It runs at about 750 cycles per second on my computer and has yet to fail me.
    Code:
    #include <iostream>
    #include <ctime>
    #include <iomanip>
    #include <windows.h>
    
    int main() 
    {
    	clock_t dwStart, dwCur;
    	int iCycles = 0;
    	double fCPS;
    	int iFaults = 0;
    	dwStart = clock();
    	std::cout << std::fixed << std::setprecision(2) << std::showpoint;
    	while(1)
    	{
    		keybd_event(VK_RETURN, 0, 0, 0);
    		if((GetAsyncKeyState(VK_RETURN) & 0x8000) == 0)
    		{
    			std::cout << "Fault\n";
    			iFaults++;
    		}
    		if((GetAsyncKeyState(VK_RETURN) & 0x8000) == 0)
    		{
    			std::cout << "Fault\n";
    			iFaults++;
    		}
    		keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);
    		if((GetAsyncKeyState(VK_RETURN) & 0x8000) != 0)
    		{
    			std::cout << "Fault\n";
    			iFaults++;
    		}
    		if((GetAsyncKeyState(VK_RETURN) & 0x8000) != 0)
    		{
    			std::cout << "Fault\n";
    			iFaults++;
    		}
    
    		iCycles++;
    		dwCur = clock();
    		fCPS = static_cast<double>(iCycles) * CLOCKS_PER_SEC / static_cast<double>(dwCur - dwStart);
    		
    		std::cout << "Cycles per second: " << fCPS;
    		std::cout << "\t\tTotal Faults: " << iFaults << "\n";
    	}
    	return 0;
    }
    And lastly. WM_KEYDOWN is not 60 FPS game material. You could make it work, but it won't be pretty or efficent.
    Last edited by Brad0407; 04-09-2007 at 01:05 PM.
    Don't quote me on that... ...seriously

  12. #12
    Registered User OnionKnight's Avatar
    Join Date
    Jan 2005
    Posts
    555
    No it's really the other way around, GetAsyncKeyState() does not care about repeats. When called it will return immediately telling you whether the key is down right now or not. WM_KEYDOWN on the other hand is sent whenever a key is pressed and if you hold it down there will be more WM_KEYDOWNs sent based on the repeat count in windows. When the key is no longer held down a WM_KEYUP will be sent.

    As for multiple keys held at the same time there will be a WM_KEYDOWN sent seperately for each key held so you can't tell if any other key is held down during one call. What you can do and what is a common way to solve the problems of repeats (or no repeats at all) is that when a WM_KEYDOWN message is recieved the status of that character which is 'down' is stored to an array of bools, usually as true, with unpressed keys being stored as false.

    Basically
    Code:
    bool keys[256]; /* there are 256 virtual keys ranging from 0-255 so this is pretty safe */
    
    /* when handling the messages */
    case WM_KEYDOWN:
        keys[wparam] = true;
        break;
    case WM_KEYUP:
        keys[wparam] = false;
        break;
    
    /* later when you need to check the keys it's as simple as */
    if (keys[VK_UP])
        TestImage.y--;
    This way you won't be troubled with repeats.

  13. #13
    Math wizard
    Join Date
    Dec 2006
    Location
    USA
    Posts
    582
    I'm now getting really confused as I'm being told two different things and what I'm actually getting doesn't match anything.

    Look at the code sample in the first message of this thread. This doesn't use WM_KEYDOWN, only GetAsyncKeyState and that only. In my test, when I pressed the up arrow key, I expected the image to go up 1 pixel per frame, or 60 pixels per second. That's not what I got at all. What I got was that it moved 1 pixel, then stopped for 1/4 second, then it moved 1 pixel every 2 frames until I let go of the key. This matches my keyboard repeat and delay settings, the highest possible settings.

    Also, note that I'm using C rather than C++ so instead of "bool", it'll likely have to a char instead (a char array of 256 or 16 but if using 16, using bitwise operators). I don't know what the double colon or "<<" is, but I suspect they're C++ operators since I only know C at the moment.

  14. #14
    Captain - Lover of the C
    Join Date
    May 2005
    Posts
    341
    >> I expected the image to go up 1 pixel per frame, or 60 pixels per second. That's not what I got at all.
    Because you were using it wrong! Use it right then decide if GetAsyncKeyState is what you want.
    Don't quote me on that... ...seriously

  15. #15
    Math wizard
    Join Date
    Dec 2006
    Location
    USA
    Posts
    582
    Sorry if I'm bothering you, but when I'm confused and frustrated, I get like this. How is GetAsyncKeyState used properly (in C rather than C++)?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 16
    Last Post: 11-23-2007, 01:48 PM
  2. Check state of buttons
    By Sober in forum Windows Programming
    Replies: 2
    Last Post: 05-03-2007, 02:23 PM
  3. input/switch statement issues
    By peanut in forum C Programming
    Replies: 5
    Last Post: 10-27-2006, 02:58 PM
  4. About aes
    By gumit in forum C Programming
    Replies: 13
    Last Post: 10-24-2006, 03:42 PM
  5. BST/Red and Black Tree
    By ghettoman in forum C++ Programming
    Replies: 0
    Last Post: 10-24-2001, 10:45 PM