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);
Related
I have a piece of code that performs some keyboard event functions:
void
KeyboardCallback(GLFWwindow */*window*/, int key, int /*scancode*/, int action,
int /*mods*/)
{
if (( action == GLFW_PRESS|| action == GLFW_REPEAT ) && key == GLFW_KEY_X) {
camera_z = camera_z + (0.1*camera_z);
}
if (( action == GLFW_PRESS|| action == GLFW_REPEAT ) && key == GLFW_KEY_Z) {
if (camera_z >= 0.01) {
camera_z = camera_z - (0.1*camera_z);
}
}
}
However, the output only appears once I've clicked on the window once. Why is this happening? Is there a way to rectify this?
It seems, that your window does not have the input focus. Normally, the window will get the focus when it is shown on screen.
You're able to control this behaviour via window hints (before creation):
GLFW_FOCUSED specifies whether the windowed mode window will be given input focus when created
GLFW_FOCUS_ON_SHOW specifies whether the window will be given input focus when glfwShowWindow is called
To set the input focus at any time, call: glfwFocusWindow
Currently, when holding down the desired key, the input registers multiple times. Is there a way to process only the first event after the key is pressed and ignore the following events until the key is released?
I'm using a processInput function, with the following condition:
if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) {
currentXPos--;
if (currentXPos < 0)
currentXPos = 0;
}
currentXPos is just an integer that's affected by the left/right arrow keys. I have an equivalent currentYPos integer also that's affected by the up/down arrow keys. I need to increment/decrement currentXPos once per key-press. I've tried adding a global bool initially set to true and on execution setting it to false, like this:
if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) {
if (canMove) {
canMove = false;
currentXPos--;
if (currentXPos < 0)
currentXPos = 0;
}
}
if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_RELEASE) {
canMove = true;
}
This does work with the single key, but if I implement this functionality with the right arrow key as well, (for incrementing the same value), the below function constantly returns GLFW_RELEASE after the first release, setting the canMove bool to true and ultimately making it redundant;
if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_RELEASE) {
canMove = true;
}
I've tried using glfwWaitEvents() but that still processes multiple inputs when help for longer than 0.5 seconds or so (the same effect as holding down any character on the keyboard in a search bar/text editor).
When you want to handle every key just once, the best solution is to listen to the key callback event instead of querying the key state in every frame. The key callback is a function that can be hooked into glfw and is called once for every key event.
The callback should look somehow like this:
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == GLFW_KEY_RIGHT && action == GLFW_PRESS)
{
currentXPos--;
if (currentXPos < 0)
currentXPos = 0;
}
}
This callback can then be registered somewhere after the point where the window has been created:
glfwSetKeyCallback(window, key_callback);
More details can be found in the GLFW Input Guide.
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.
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
}
Here is the definition of my keyboard callback method I'm using to check for user input for a 2D basic game. However I'm having a problem handling certain simultaneous key presses.
For example, If I hold the Right and Up arrows keys, the player moves 45 degrees toward the top right of the screen as it should. Then, while still holding the up and right keys, if I press Space (which fires a projectile), that works as well.
However, if I hold the Left and Down arrow keys, the player moves as it should, but when I press Space, I get no input response, so I can't fire a projectile when moving down and left. All other movement + fire projectile combinations work, just the down and left doesn't... I can't figure out why. Any ideas?
if (key == GLFW_KEY_LEFT)
{
GameController::getInstance()->getPlayer()->changeKeyPress(GLFW_KEY_LEFT, action);
}
else if (key == GLFW_KEY_RIGHT)
{
GameController::getInstance()->getPlayer()->changeKeyPress(GLFW_KEY_RIGHT, action);
}
else if (key == GLFW_KEY_UP)
{
GameController::getInstance()->getPlayer()->changeKeyPress(GLFW_KEY_UP, action);
}
else if (key == GLFW_KEY_DOWN)
{
GameController::getInstance()->getPlayer()->changeKeyPress(GLFW_KEY_DOWN, action);
}
else if (key == GLFW_KEY_SPACE)
{
GameController::getInstance()->getPlayer()->changeKeyPress(GLFW_KEY_SPACE, action);
}
else { }
Rollover is the property that allows a keyboard to properly register many key presses at once. Keyboards are wired in rows and columns. Even if the keyboard is not square, the individual keys are in a roughly square matrix of wires connecting their switches. The controller connects one row and then tests to see which of the columns are hit. Some key combinations "shadow" others. The controller can tell when the input is ambiguous and send no keys.
Better keyboard use diodes arranged to avoid the ambiguity and thus support "full rollover", although in practice USB limits you to all the modifiers plus 6 distinct keycodes at once.
Sounds like a crappy keyboard.
You may have to procure a better one.
Or use different key combinations.
The code should be
if (key == GLFW_KEY_LEFT)
{
GameController::getInstance()->getPlayer()->changeKeyPress(GLFW_KEY_LEFT, action);
}
if (key == GLFW_KEY_RIGHT)
{
GameController::getInstance()->getPlayer()->changeKeyPress(GLFW_KEY_RIGHT, action);
}
if (key == GLFW_KEY_UP)
{
GameController::getInstance()->getPlayer()->changeKeyPress(GLFW_KEY_UP, action);
}
if (key == GLFW_KEY_DOWN)
{
GameController::getInstance()->getPlayer()->changeKeyPress(GLFW_KEY_DOWN, action);
}
if (key == GLFW_KEY_SPACE)
{
GameController::getInstance()->getPlayer()->changeKeyPress(GLFW_KEY_SPACE, action);
}
The problem is your code just detect 1 key press at a time so when you press left and right at the same time only the if (key == GLFW_KEY_LEFT) is fired