I'm trying to make my game do something when I press a key. I'm using GLFW to handle Windows API for me. I got stuck and confused when I used the mention callback function in title.
Referenced doc: https://www.glfw.org/docs/latest/input_guide.html#input_keyboard
I followed "Key input" resources (not "Text input"), but I get a kinda text input callback curve, like, when I keep pressed a key, I get pressed key event, then it stops for some time, and then I get repeated events. Shouldn't callbacks be constant in time (without that time gap)? This is my code:
void onKeyPress(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == GLFW_KEY_W && action != GLFW_RELEASE)
{
camera.pos += glm::vec3(0.0f, 0.0f, 1.0f);
updateCameraMatrices();
}
}
// ...
glfwSetKeyCallback(window, onKeyPress);
Nvm. It seems like my expectations were wrong. I should just listen for GLFW_PRESS and GLFW_RELEASE, store the key status, and use if(status) in my main loop.
There's no need to use event callbacks at all if you just want to know wether a key is pressed or not. Just use glfwGetKey(window,key) to get the current key status (this returns GLFW_PRESS or GLFW_RELEASE). See here for more details.
Related
I am using GLFW for keyboard input but the processing happens too quick thus my boolean switch on a single press gets changed like 10 times, as input is processed every frame. All I need that for a single press of space bar it would switch the state. My current code is below:
if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS)
{
show = !show;
}
Is there a better way of doing this?
Yes. glfwGetKey is meant to be used for continuous key input. GLFW manual lists glfwSetKeyCallback as a better alternative if you want one-time notification about key press.
Thus, for your case it'd be something like this:
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == GLFW_KEY_SPACE && action == GLFW_PRESS)
show = !show;
}
If you don't want to use this method for whatever reason, you can always implement a similar thing yourself. You'll need a boolean value (or array of values) representing the key state. Then, in your input handling, you must only react on the change of the button state, like so:
bool spacePressed;
// in handling
bool spaceCurrentlyPressed = glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS;
if (!spacePressed && spaceCurrentlyPressed) { // wasn't before, is now
show = !show;
}
spacePressed = spaceCurrentlyPressed;
I reccommend using GLFWs key callbacks instead of getting the key state yourself every frame. This way you will only receive one keypress and one keyrelease event per key.
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if(action == GLFW_RELEASE) return; //only handle press events
if(key == GLFW_KEY_SPACE) show = !show;
}
//in main or init
glfwSetKeyCallback(window, key_callback);
I have a QT Application on Windows which has a mode of using arrow keys, and also a mode which should totally ignore these arrow keys. That is, I want the arrow keys to not to trigger any event once the user checks a box.
I saw a post where eventFilter() was suggested, but I did not get how I could use it. Here is the checkbox event that listens the user, and gets triggered once the user checks it. In the else part I want the eventFilter() to work for arrow keys, but so far I could not get it running.
void MainWindow::on_checkBoxSmartCutMode_stateChanged(int arg1)
{
if (arg1 == 0)
{
// do as usual, arrow keys should work
}
else
{
eventFilter(); // if any arrow key is pressed, ignore the event
}
}
Any suggestions?
You can use keyEvent as your key filter by override keyPressEvent and test your checkbox state.
example:
void MainWindow::keyPressEvent(QKeyEvent *event)
{
// check your checkbox state
if (ui->poCheckBox->checkState() == Qt::Unchecked)
// do as usual, arrow keys should work
return;
switch(event->key())
{
case Qt::Key_Left:
case Qt::Key_Right: // add more cases as needed
event->ignore(); // if any arrow key is pressed, ignore the event
return;
}
// handle the event
}
So, I'm currently working on an options menu for my game, I have a button that when pressed it changes it's text to the next resolution in an array, so basically the user presses this button to change their resolution to the next string in the array.
My problem is getting the click event.
Right now, when the user presses the button, it returns true while the mouse is down, instead of when the mouse is pressed. I want to only return true in the mouse event when the mouse is pressed.
I've looked around, and everything I've found seems to be similar to what I've done or, as I said, returning true while the mouse is down, instead of the initial click.
My events are handled in a EventManager singleton, and here are the functions that I see as necessary:
My update function, this is where the event is polled, it is worth noting I'm using a private SDL_Event named "e".
void EventManager::update(){
while(SDL_PollEvent(&e)){
SDL_GetMouseState(&mouseX, &mouseY);
switch(e.type){
case SDL_QUIT:
running = false;
}
}
}
My mousePress function, where I want a mouse press returned.
int EventManager::mousePress(){
if(e.type == SDL_MOUSEBUTTONDOWN){
return e.button.button;
}
return 0;
}
Instead of using SDL_GetMouseState(), which gets you the actual state of the mouse (thats probably where its name comes from ;) ), use the event you are polling. SDL should give you a SDL_MouseButtonEvent which contains the informations you need and should only be queued once.
See https://wiki.libsdl.org/SDL_MouseButtonEvent
Edit to clarify what i mean:
You would use something like this:
void EventManager::update(){
SDL_Event e;
while(SDL_PollEvent(&e)){
switch(e.type){
case SDL_QUIT:
running = false;
break;
case SDL_MOUSEBUTTONDOWN:
//do whatever you want to do after a mouse button was pressed,
// e.g.:
mousePress(e.button);
break;
}
}
}
Inside your mousePress-Function you can then test, which of the mouse buttons has been pressed:
void EventManager::mousePress(SDL_MouseButtonEvent& b){
if(b.button == SDL_BUTTON_LEFT){
//handle a left-click
}
}
This works, because SDL_PollEvent will only return exactly once for every Event. If theres no new Event, it will return an empty Event. So 1 click = 1 times SDL_PollEvent() with e being of type SDL_MOUSEBUTTONDOWN afterwards and 1 times SDL_PollEvent() with e being of type SDL_MOUSEBUTTONUP afterwards. If you call SDL_PollEvent() in between or afterwards, it will return 0 and leave e as an Empty Event, not calling the switch at all. If you respond to MOUSEBUTTONDOWN or MOUSEBUTTONUP or both is up to you...
I've also declared the SDL_Event a local variable to update(). Why? The Idea behind an Event is, that theres an Event-Object whenever some event has occured. Then you react to the event and forget about it. So theres no need to have a global variable. If you want to prevent constant construction/destruction, you can also declare it to be static. But thats just some hint, not related to your original question.
I am currently dealing with a multi-form application and am having issue registering a del key press, the application that requires the del key is a form with a frame on it with objects painted on it that can be selected, upon pressing the del key the selected objects are to be deleted via a deleteObjects method. The code I am currently using is as follows
void __fastcall TF_Image::KeyUpKbd( WORD &Key )
{
if(Key == VK_DELETE || Key == VK_DKEY)
{
deleteSelectedObjects();
}
}
(Note: There are other paramenters in the function call but they aren't used)
TF_Image inherits from TFrame
I have tried mapping other keys other than the del key ie the D key and have found that the method is called with no problem. I have discovered that when pressing (physically) the del key the methods associated with KeyUp & KeyDown are not called.
Edit: So i've attempted to add the DeleteSelectedOb() method to my WndProc method without much luck either.
void __fastcall TF_ImgBrowserOA::WndProc(TMessage &Message)
{
if (Message.Msg == WM_KEYDOWN)
{
if (Message.WParam == VK_DELETE)
{
F_Image->DeleteSelectedOb();
}
}
//code that manages window resize
TForm::WndProc(Message);
}
The WndProc method doent appear to respond to keystrokes
So after cleaning up some code in some other modules and removing unneccessary menu's I decided to go back and look at this section again after I found a similar piece of code implementing a similar function, I couldn't see much difference between them and so I recompiled and attempted to run my Delete function from the KeyDown event and for some reason it just worked, I suspect it came down to an issue of another element holding focus in the Application. As a precaution I also called a SetFocus() to the frame in which I required this code to operate in. Its still a mystery to me why this didn't work intially though.
Here is a snippet for my TRichEdit control (Script_Edit).
TWndMethod *PrevWndProc = Script_Edit->WindowProc;
Script_Edit->WindowProc = MyWndProc;
void __fastcall My_Form::MyWndProc(TMessage &Message) {
switch (Message.Msg) {
case WM_KEYDOWN: {
// Check for DELETE and BACKSPACE keys
if( Message.WParam == VK_BACK ||
Message.WParam == VK_DELETE
) {
// Do whatever you need
}
break;
default:
// call default handler if not processed
PrevWndProc(Message);
}
}
You can't get much closer to the message core than this with VCL...
I'm using Freeglut to design some basic games. However, I have been having some issues with keyboard input.
In some previous games I made, I had did something like this: (pseudocode)
class Key {
bool pressed;
void press() {pressed = true;}
void release() {pressed = false;}
bool isPressed() {return pressed;}
}
Key wkey, skey;
handleKeypress(unsigned char key, int xf, int yf) { //This is the function that glutKeyBoardFunc gets
switch(key) {
case 'w':
wkey.press();
case 's':
skey.press();
}
}
handleKeypress(unsigned char key, int xf, int yf) { //This is the function that glutKeyBoardUpFunc gets
switch(key) {
case 'w':
wkey.release();
case 's':
skey.release();
}
}
When I wanted to check whether or not a key was pressed, I checked wkey.isPressed(). However, this caused issues. For example, the Esc key was supposed to pause the game, and then pressing Esc from the pause screen was supposed to take the user to the main menu. However, pressing Esc directly took the user to the main menu because the user didn't release the escape key in that tick of the main loop.
To avoid this kind of issue, what is the best way to take (and use) keyboard input with Freeglut?
EDIT: By the way, this is C++
How about you save a previous key state and a current key state for each frame? This way you can test if the previous key state was KEY UP and the new key state is KEY DOWN. This indicates that the key has been pressed. You can adopt this for a "key pressed" function - just test to see if the previous key state was KEY DOWN and the new key state is also KEY DOWN. A "key up" function would test if the previous key state was KEY DOWN and the new key state is KEY UP.