Handling input with GLFW - c++

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

Related

GLFW key input treatment [duplicate]

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.

How do I assign a value in an array when a mouse is pressed SFML C++

I am trying to make a program where you are allowed to select between an option of shapes, and then drawing it. To allow for multiple shapes I created a vector of a class which creates shapes (Shapes are set up with the chosen function). My problem is the mouse click is too long, so it assigns it to everything in the vector, so you can't create a new shape. Is there a problem in my logic, or is there a problem in the code?
Here is my attempt:
for (auto& it : onCanvas) {
if (Mouse::isButtonPressed(Mouse::Left)) {
if (mousepointer.getGlobalBounds().intersects(circleOption.getGlobalBounds())) {
it.chosen(circles);
}
if (mousepointer.getGlobalBounds().intersects(rectOption.getGlobalBounds())) {
it.chosen(rectangle);
}
if (mousepointer.getGlobalBounds().intersects(triOption.getGlobalBounds())) {
it.chosen(triangles);
}
if (mousepointer.getGlobalBounds().intersects(it.shape.getGlobalBounds()) || it.dragging) {
it.shape.setPosition(mousepointer.getPosition());
it.dragging = true;
}
}
if (!Mouse::isButtonPressed) {
it.dragging = false;
}
win.draw(it.shape);
}
Your source-code is a bit incomplete (what is onCanvas and mousepointer). But I guess the problem is that this snippet is called multiple times while your mouse is clicked. To avoid that you can do two thing.
In the first solution you use events, so you only add shapes when the state of the mousebutton changes (you can additionally listen to the MouseButtonReleased to simulate a full click):
if (event.type == sf::Event::MouseButtonPressed)
{
if (event.mouseButton.button == sf::Mouse::Left)
{
// Hit Detection
}
}
or second solution you remember the last state of the button (probably do the mouse check once outside of the for loop):
bool mouse_was_up = true;
if (mouse_was_up && Mouse::isButtonPressed(Mouse::Left)) {
mouse_was_up = false;
for (auto& it : onCanvas) {
// Hit Detection
}
}
else if (!Mouse::isButtonPressed(Mouse::Left))
mouse_was_up = true;
I would rather stick to the first solution because when your click is too short and your gameloop is in another part of the game logic, you can miss the click.

QT: Ignore key events on checkbox selection

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
}

SDL_KEYDOWN triggering twice

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.

How to catch Ctrl+C key event with Qt when Ctrl is released before 'C'?

I would like to call some custom copy code when the user releases Ctrl+C. When C is released before Ctrl, Qt sends a key event that matches with QKeySequence::Copy. When Ctrl is released before C, the release event does not match.
When the key release event comes in with Ctrl, is there a way to see if C is still being held down?
When I don't handle Ctrl being released first, the event gets passed along and it does a regular copy, which is exactly what I don't want to happen.
bool
MyWidget::eventFilter(QObject* object, QEvent* event)
{
// the text edit box filters its events through here
if (object == m_text_edit_box)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *key_event = static_cast<QKeyEvent*>(event);
if (key_event->matches(QKeySequence::Copy))
{
// don't do anything and don't pass along event
return true;
}
}
else if (event->type() == QEvent::KeyRelease)
{
QKeyEvent *key_event = static_cast<QKeyEvent*>(event);
if (key_event->matches(QKeySequence::Copy))
{
// we only get in here if 'c' is released before ctrl
callCustomCopy();
return true;
}
}
}
// pass along event
return false;
}
You could query the letter 'C' and the meta key Ctrl specifically and not rely on key_even->matches(). you can of course in the object where you located the eventfilter on the keydown event store the fact wether the keydown sequence did match copy.
This (untested) might work for you, note that the static variable should be a member variable of the class that this is contained in, this just seemed clearer in the context of this example. The exact logic of what you want to accomplish might need more state information to be carried between events.
bool MyWidget::eventFilter(QObject* object, QEvent* event)
{
// Remember state between events
static foundCopy = false;
// the text edit box filters its events through here
if (object == m_text_edit_box)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *key_event = static_cast<QKeyEvent*>(event);
if (key_event->matches(QKeySequence::Copy))
{
foundCopy = true;
// don't do anything and don't pass along event
return true;
}
else
{
foundCopy = false;
// This is another sequence, ignore and pass event
// Note that this will trigger with ctrl+c+a and others
}
}
else if (event->type() == QEvent::KeyRelease)
{
QKeyEvent *key_event = static_cast<QKeyEvent*>(event);
if (foundCopy)
{
callCustomCopy();
foundCopy = false;
return true;
}
// This should keep the system copy from triggering
if (key_event->matches(QKeySequence::Copy))
{
return true;
}
}
}
// pass along event
return false;
}
Another way would be to collect the actual state of all the keys pressed at the current time and then when one is released see which ones are still pressed.
From a UI point of view please bear in mind that all keyboard actions are performed on press, (e.g. typeing, windows paste), performing actions on release in general might confuse the user, especially when there is a visible result to the action. I can't tell from your example what you are trying to accomplish.