Link to home
Start Free TrialLog in
Avatar of List244
List244

asked on

Console best way to check keys.

     while (5!=4)
      {
            switch (GetKeys())
            {
            case 1:
                  ShowMenu(1);
                  break;
            }
      }

I have a loop here, it runs through, checking keys. Problem is if they press Escape
(Case 1) It loads the menu. But, when the menu loads, it also receives the Escape
key. The escape key is unknown to the menu however, and a message flashes many
times saying unknown command.

GetKeys() is a function using GetKeyState. Inside ShowMenu Getch is used. I would like
somehow to have repeat commands ignored if pressed within one second of the last
time it was pressed. I have tried the sleep function, however this does pause for 1
second, after resumes all keys are then sent, and still the Unknown command
message is flooded.

Unknown command is a self written message, not an error. It simply is there to tell
the user that the button they pressed has no affect on the menu. My question is
how can I handle the key presses in such a way that they will not flood my program,
BUT I will know if any key is pressed at any time. I want delay for SAME key only,
not new keys.
ASKER CERTIFIED SOLUTION
Avatar of mrwad99
mrwad99
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of List244
List244

ASKER

Okay, I have changed it to check if Keystate <0 then to wait for it to
be 0. This seems to have helped, a little. It has solved the problem
of spamming keys.

However, at times keys are ignored. Sometimes I have to hit
keys up to 20 times before it realizes I pressed anything. And
in my main game loop, as of now all that runs is keycheck.
Which means if I add more, the key check is going to be
even less reliable. How can I make it more reliable, so that
users will not be frustrated by having to hit keys so many times
before affect takes place?

(If code for the key check is necessary, I will post it)

However, basically it is
{
If (keystate(Ofexpectedkey1)) <0 {
wait for 0}
If (keystate(Ofexpectedkey2)) <0 {
wait for 0}
If (keystate(Ofexpectedkey3)) <0 {
wait for 0}
If (keystate(Ofexpectedkey4)) <0 {
wait for 0}
}
Post the whole code.  

This will save endless time in others knocking up dummy routines when the best thing to do is simply work on your code.
What's the OS?
Due to GetKeyState I am assuming Windows...
Avatar of List244

ASKER

I am using Windows XP Pro.

I have found the MAIN issue. It now is more understandable what the problem
truly is. Now, with the code I am going to post, the problem is, each key must
be hit twice. So if Escape should do something, it takes 'Escape' 'Escape' to
do it:

void PlayGame()
{
      while (5!=4)//Start a game loop
      {
            cout.flush();
            switch (GetKeys())
            {
            case 1:
                  ShowMenu(1);
                  break;
            }
      }
}

int GetKeys()
{
if (GetKeyState(VK_ESCAPE)<0)
{
      while (GetKeyState(VK_ESCAPE)!=0) {}
      return 1;
}
if (GetKeyState(76)<0)
{
      while (GetKeyState(76)!=0) {}
      return 108;
}
if (GetKeyState(83)<0)
{
      while (GetKeyState(83)!=0) {}
      return 115;
}
if (GetKeyState(81)<0)
{
      while (GetKeyState(81)!=0) {}
      return 113;
}
if (GetKeyState(80)<0)
{
      while (GetKeyState(80)!=0) {}
      return 112;
}
return 0;
}

int ShowMenu(int MenuID)
{
switch (MenuID)
{
int Mchar;
case 1://Main Menu
      cout<< "GAMENAME.\n";
      cout<< "What would you like to do?\n";
      cout<< "(P)lay now\n";
      cout<< "(Q)uit\n";
      cout.flush();
Mlabel:
      Mchar =0; //make it have you choose again
      Mchar = GetKeys();
            switch (Mchar)
      {
            case 108: //l
                  cout <<"Load Game\n";
                  return 1;
                  break;
            case 115://s
                  cout <<"Save Game\n";//SAME AS LOAD (continue game)
                  return 2;
                  break;
            case 113://q
                  cout <<"Quit Game\n";
                  exit(-1);
                  return 3;
                  break;
            case 112: //p
                  cout <<"Play Game\n";///SAME AS LOAD
                  return 1;
                  break;
            default:
                  if (Mchar==0) {goto Mlabel;}
                  cout<< "I did not understand that input\n";
                  cout.flush();
                  goto Mlabel;
                  break;
            }
      break;
default:
      break;
}
cout.flush();
return 0;
}
Your code is killing the processor because it loops infinitely in GeKeys(); the problem is it does not wait for user input.  A better way to do this would be to use getch().  I take it that you don't want to have to bother with pressing enter, that is why you have not used cin.

You can adapt your code easily using the following concept:

char c = getch(); // wait for input

if (cS == VK_ESCAPE)
      // Escape key was pressed...

You can read ASCII codes/virtual key codes/normal characters with getch() as shown above.

To use getch(), #include <conio.h>

HTH
Avatar of List244

ASKER

It is true I do not want to wait for enter.
However, I also do not want to wait for
a key. I am scanning for keys, however
if no key is pressed the program must
continue, this program will run REALTIME.

This is why I have an infinite loop, that
stops ONLY if a button is being pressed
and only to wait for release.
>> if no key is pressed the program must
continue, this program will run REALTIME.

Right, well that is not being accomplished at the minute.  What is happening with your current code ?  This:

main()
{
  // effectively hang on switch (GetKeys())
}
so we jump into GetKeys():
{
// effectively run in an infinite loop waiting until a key is pressed.  
}

Control in main() never gets passed the swich statement, because GetKeys() does not return anything until a key is pressed.  So you are looping and looping and getting precisely squat out of it.

You need to use getch() as I have mentioned.
.. and although GetKeys()  has

return 0;

that does not matter as 0 is never handled by the switch in PlayGame(), not even via a

default:

branch.

So the loop continues, as I have said above.
Avatar of List244

ASKER

Get keys does not hang up. Get keys runs, checks 5 or so keys,
if they are pressed it waits for release, if not, it continues.
The GetKeyState function checks whether or not a specified
key is pressed. So, there is no way to hang up.
Upon retrieval of 0, the switch statement is broken, and the
loop continues. However, the loop is the game loop, not the
keysearch loop, so the loop is a wanted loop, and there is
NO hang up in GetKeys()

How this works is:

Playgame ()
{
Do
 CheckKeys()
  If key is 1 open menu else continue
Loop
{
Avatar of List244

ASKER

I have found the problem:

if (GetKeyState(VK_ESCAPE)<0)
{
      while (GetKeyState(VK_ESCAPE)!=0) {}
      return 1;
}

Should be:

if (GetKeyState(VK_ESCAPE)<0)
{
      while (GetKeyState(VK_ESCAPE)<0) {}
      return 1;
}

The reason being, when the key is released GetKeyState
can be greater than 0, but will for sure be 0 or more.
Avatar of List244

ASKER

Thank you however, for pointing me in the right direction.
I got it all working great now. No more pressing keys
twice, and such things.
Presumably you need to be able to poll the keyboard non-blockingly and need to know if there is a key waiting.

Something like:

        while (true) {
                // Non-blocking poll of keyboard
                if (_kbhit()) {
                        int key = _getch();
                        if (key == 0 || key == 0xe0) { // Function or arrow key?
                                key = _getch();
                                handle_function_or_arrow_key(key); // Handle the arrow key
                        }
                        else
                                handle_ordinary_key(key);
                }
                // .. Do your other stuff
        }

Check out http://msdn.microsoft.com/library/en-us/vclib/html/_crt__kbhit.asp
OK.  If none of those keys are pressed, GetKeys() does return 0.   Fine.  

>> Upon retrieval of 0, the switch statement is broken,

void PlayGame()
{
     while (5!=4)//Start a game loop
     {
          cout.flush();
          switch (GetKeys())
          {
          case 1:
               ShowMenu(1);
               break;
          }
     }
}

How can that be ?  There is no handler for 0, or even 'default' in this switch statement.  I have ran and compiled the code you posted above, and all that happens is that the switch statement runs into infinity as a result of GetKeys() not returning 1 until the ESC key is actually pressed.

But if it works now, who am I to moan...
And thanks for the points :)
Avatar of List244

ASKER

I'm not sure, it may be compiler differences. But, upon 0 mine skips
right out of the switch statement, and continues right to cout.flush();

Thanks for the help.
Glad to help.  Btw, what compiler are you using ?
Avatar of List244

ASKER

I am using Vc++ 6.0
So am I.  

while (5!=4)  // Start a game loop
{
     switch (GetKeys())
     {
     case 1:
          ShowMenu(1);
          break;
     }
}


There is no way that switch statement will do anything unless 1 is returned from GetKeys().

Oh well.  At least the code works.
Avatar of List244

ASKER

#include <iostream.h>
int GetKeys();

int main()
{
switch (GetKeys())
      {
      case 1:
            cout<< "T";
      break;
}
cout<< "Closing game...Press any key to continue";
cout.flush();
int T;
cin >> T;
return 0;
}

int GetKeys()
{
return 0;
}


Run that as a program.

The output will be :

Closing game...Press any key to continue
Heh.  Yeah I can see that.  But what is missing from this code that is not in the example you posted ?  Here, you are missing the while loop, that you have implemented as infinite via the (strange) method of while (5!=4).

while (5!=4)  // Start a game loop <----  This while loop is the key difference
{
     switch (GetKeys())
     {
     case 1:
          ShowMenu(1);
          break;
     }
}

See ?  The switch statement is executed once; if 1 is returned from GetKeys(), ShowMenu(1) is called, and the switch breaks.  But this is still within the while loop, so the switch is entered again.

If 1 is *not* returned from GetKeys(), nothing happens in the switch statement, and the while loop starts again.  So, when no key is being pressed, the CPU is burning cycles on the infinite while loop because nothing is being returned from GetKeys().  This is why I suggested using getch(); this actually pauses execution until a key is read, hence no cycles are wasted.

You say the justification of coding like this, and not using getch() was something to do with needing the program to run in "real time".  If that is the case, you need to be using two threads, *certainly* not doing it this way.  But I still cannot see why this program would need to be real time anyway...
Avatar of List244

ASKER

If you could see the final project, you would see why it is real time.
It is early in development right now, and looks a little odd, I agree.
The while loop is meant to continuously waste cycles. Once again,
early development. Many more things will be inside of that loop
upon completion. As of now, I am just getting the basics worked
up.
OK.  Good luck with the final project :)