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.
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
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 am following lazy foo's tutorial, however I realized every time I press press s or p, SDL_KEYDOWNtriggers twice. How can this be fixed?
Here is the code snippet:
while(SDL_PollEvent(&e) != 0) {
if(e.type == SDL_QUIT) {
quit = true;
}
else if(e.type == SDL_KEYDOWN) {
if(e.key.keysym.sym == SDLK_s) {
if(timer.isStarted()) {
timer.stop();
printf("stop\n");
}
else {
timer.start();
printf("start\n");
}
}
else if(e.key.keysym.sym == SDLK_p) {
if(timer.isPaused()) {
timer.unpause();
printf("unpause\n");
}
else {
timer.pause();
printf("pause\n");
}
}
}
}
Pressing s once:
start
stop
TL;DR: Check if e.key.repeat equals to 0 before handling the events.
SDL generates fake repeated keypresses if you hold a key long enough. This is used mostly for text input.
The original key press has .repeat == 0, and fake presses have .repeat == 1.
For convenience reasons probably (I'd argue that it's rather inconvenient), since SDL 2.0.5 the actual key press generates two events instead of one. One has .repeat set to 0, and other (new) one has it set to 1.
I'm trying to use ncurses to create a game. I set it so that the character moves by arrow key input, but if I hold the arrow key for a while and then let go the character will keep moving for a while before stopping.
These are my initializations:
initscr();
start_color();
cbreak();
noecho();
nodelay(stdscr,TRUE);
keypad(stdscr, TRUE);
This is my main loop:
while(1) {
...
if (key == (char)27) {
break;
}
else if (key == KEY_DOWN) {
key = 0;
player->advance(0, 1);
}
else if (key == KEY_UP) {
key = 0;
player->advance(0, -1);
}
else if (key == KEY_LEFT) {
key = 0;
player->advance(-1, 0);
}
else if (key == KEY_RIGHT) {
key = 0;
player->advance(1, 0);
}
else {
key = getch();
}
std::this_thread::sleep_for(std::chrono::milliseconds {1000/30});
}
I'm using the sleep_for because I noticed that if I use timeout then the framrate changes if I'm pressing a key. Using sleep_for creates a consistent framerate, but input is somehow "sticky" as I explained. I need to fix this somehow. Either get a consistent framrate using timeout, or "un-stick" the input when using sleep_for.
player->advance(int, int) moves the player one step in the specified direction. Direction is specified as change in x and change in y to be applied to the current position.
You could call flushinp after each call on getch, to ignore type-ahead.
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