SFML community forums

Help => System => Topic started by: Glocke on October 08, 2014, 10:27:25 am

Title: [SOLVED] Input handling: detect holding two keys
Post by: Glocke on October 08, 2014, 10:27:25 am
Hi, since I introduced diagonal movement to my game, I encountered problems while detecting. Input handling works currently this way: The mainloop forwards the sf::Event (if type is KeyPressed or KeyReleased) to the GameObject's input component. At this component, I store this press/release in a (as map<Key, State>). On each cycle, my component is updated to move all previously pressed keys to currently hold - and previously released keys to currently "free" keys.

This system works great, unless one fact: When querying my input component, all predefined keys (the bindings for left,right,up,down and some additional ones - doesn't matter here) are checked. Key detection works this way:

if (keys.at(binding.up) == PRESS || keys.at(binding.up) == HOLD) {
    move.y = -1;
}
Left, down and right are handled in the same way. So the move var (btw sf::Vector2i) finally has values such es (0,1), (-1,1) etc. This works well, IF the keys are pressed EXACTLY at the same time. But often pressing A and W (as left and up) doesn't happen at the same time, because the program is even faster than the user.

Well, so I started delaying each of those queries to the input state for some milliseconds (so they aren't queried on each frame if a user is allowed to). But the effect isn't that great: Sometimes the movement looks delayed to the player (because of the delay ;D ) - and sometimes the previous effect (first walk left, than diagonally left-up) occures (because the up-key is pressed slitely after the first query).

What am I doing wrong referring to my input design? :-\

BTW: The first "left-only" movement is completly executed, because my systems avoids query input while an action (walking to the left tile) isn't completed. This system achieves some kind of discrete movement and simplifies collision detection etc.
Title: Re: Input handling: detect holding two keys
Post by: eXpl0it3r on October 08, 2014, 11:24:19 am
It's quite hard to understand how your system works in full details, but what if you just check for the hold status?

if (keys.at(binding.up) == HOLD && keys.at(binding.down) == HOLD) {
    move.y = 0;
}
else if (keys.at(binding.up) == HOLD) {
    move.y = -1;
}
else if (keys.at(binding.down) == HOLD) {
    move.y = 1;
}


if (keys.at(binding.right) == HOLD && keys.at(binding.left) == HOLD) {
    move.x = 0;
}
else if (keys.at(binding.left) == HOLD) {
    move.x = -1;
}
else if (keys.at(binding.right) == HOLD) {
    move.x = 1;
}
Title: Re: Input handling: detect holding two keys
Post by: Glocke on October 08, 2014, 11:38:03 am
I tried this, but this just moved the problem. It's the same whether I check "W pressed|held & A pressed|held" as I check "W held & A held".

Because:

I guess the problem is about my "don't query again unless the action has been finished". Without this, the system could correct the movement direction duration the next frame (I'm currently working on this while keeping my discrete movement style).
Title: Re: Input handling: detect holding two keys
Post by: eXpl0it3r on October 08, 2014, 11:41:37 am
I guess the problem is about my "don't query again unless the action has been finished".
Not sure what that means, but it sounds wrong. You should process all events every frame.
Title: Re: Input handling: detect holding two keys
Post by: Glocke on October 08, 2014, 11:54:36 am
Not sure what that means, but it sounds wrong. You should process all events every frame.

On each action (move, attack etc.) a cooldown is set. If this cooldown is zero, the action is finished and another action can be applied. This systems prevents the physics from changing the moving direction while moving. But exactly that change would fix my problem. Unfortunately, when avoiding this, my collision detecting is broken (but easy to fix).. but the rendering is randomly flickering while moving... Well, currently I'm trying to fix that ^^
Title: Re: Input handling: detect holding two keys
Post by: Nexus on October 08, 2014, 12:08:46 pm
You shouldn't mix input and game logic. Use a vector to store the input, pass it to a game logic routine, and check there whether it is applicable. Or adjust it where needed.

Same for collision detection. When there is a collision, don't change the input vector. Rather, introduce a separate additional force that prevents the player from running into the wall.

By the way, my Thor.Actions (http://www.bromeon.ch/libraries/thor/v2.0/tutorial-actions.html) would simplify boolean combinations of multiple keys a lot.
Title: Re: Input handling: detect holding two keys
Post by: Glocke on October 08, 2014, 12:18:54 pm
You shouldn't mix input and game logic. Use a vector to store the input, pass it to a game logic routine, and check there whether it is applicable. Or adjust it where needed.

Exactly that, what came to my mind before opening the thread :D [/quote]

Waiting for another key to be pressed (to achieve a diagonal movement) is a pure input-based application. So I'm going to delay a predefined number of milliseconds for waiting for another key. If an additional key was pressed - or the time elapsed - the movement will be forwarded to the game logic.. That's my current idea.
Title: Re: Input handling: detect holding two keys
Post by: Nexus on October 08, 2014, 12:23:15 pm
Elapsed time and cooldowns is clearly a game logic feature, so why already handle it at the input stage? There, you are only supposed to catch the pure user input (from arrow keys) and translate it to a game representation (through the key bindings or Thor, if you use it).

And keep things orthogonal. 4 statements are enough to check every key, 1 additional statement can detect and adjust diagonal movement. No need to check all 8 directions...
Title: Re: Input handling: detect holding two keys
Post by: Glocke on October 08, 2014, 12:27:51 pm
Elapsed time and cooldowns is clearly a game logic feature, so why already handle it at the input stage? There, you are only supposed to catch the pure user input (from arrow keys) and translate it to a game representation (through the key bindings or Thor, if you use it).

Waiting for two keys pressed "at the same time" (user point of view) seems to me to be an input task. Game logic is handling the final move event.

And keep things orthogonal. 4 statements are enough to check every key

Well, I just extended by game to 8 directions because 4 were not enough. By the way I'm checking only 4 directions. The diagonal moves take place after e.g. the left key and the up key were checked and returned true. Then the left key will effect "move.x = -1" and the up key will effect "move.y = -1" which means move=(-1,-1).

1 additional statement can detect and adjust diagonal movement. No need to check all 8 directions...

How to detect diagonal movement else?
Title: Re: Input handling: detect holding two keys
Post by: Nexus on October 08, 2014, 12:29:39 pm
Waiting for two keys pressed "at the same time" (user point of view) seems to me to be an input task.
Yes, but for that you don't need any clocks and cooldowns...

How to detect diagonal movement else?
See here (http://en.sfml-dev.org/forums/index.php?topic=11539.msg80341#msg80341). You can still separate input and logics, though.
Title: Re: Input handling: detect holding two keys
Post by: Glocke on October 08, 2014, 12:46:20 pm
See here (http://en.sfml-dev.org/forums/index.php?topic=11539.msg80341#msg80341). You can still separate input and logics, though.

I guess that's some kind of misunderstanding. I'm querying my input exactly that way.
But sometimes the second's key isn't detected by the system as pressed - because the user isn't as fast as the computer to be able to press really at the same time. So there will be a first left-move followed by left-up :(
Title: Re: Input handling: detect holding two keys
Post by: Nexus on October 08, 2014, 01:09:16 pm
Is it tragic if the player moves left for a tenth of a second and then moves diagonally?

That's at least what I would expect as a player. I don't like the input system to meet "smart" decisions for me, if that means I am prevented from being fast. But that may depend on the game...
Title: Re: Input handling: detect holding two keys
Post by: Glocke on October 08, 2014, 01:12:56 pm
Is it tragic if the player moves left for a tenth of a second and then moves diagonally?

As mentioned before: I intend discrete movement. This means the player steps an entire tile left and then an entire tile diagonally.

That's at least what I would expect as a player. I don't like the input system to meet "smart" decisions for me, if that means I am prevented from being fast. But that may depend on the game...

I guess waiting 80-120ms for an additional key is not a problem (at least for me). The input component isn't delay this at once but over some frames (using the given elapsed time from the mainloop). So the game itself isn't slowed down.
Title: Re: Input handling: detect holding two keys
Post by: Nexus on October 08, 2014, 01:17:16 pm
Ah, okay, that makes it clear :)

Yes, then I'd probably also handle the delay in the input module. It can then pass one of eight directions to the game logic.
Title: Re: Input handling: detect holding two keys
Post by: Glocke on October 08, 2014, 01:19:43 pm
Ah, okay, that makes it clear :)

Yes, then I'd probably also handle the delay in the input module. It can then pass one of eight directions to the game logic.

Ok, fine :) As said: just a case of misunderstanding ;D
Title: Re: Input handling: detect holding two keys
Post by: Hapax on October 09, 2014, 01:32:54 am
If your system works as I read it, I wouldn't have thought that clocks would be necessary.
Assuming you're still using move.x and move.y, you could reset them both to zero before the checking and then add to it for each key. That way, if you press up and left, it become -1, -1, and if you press right and left together, x becomes zero.
Title: Re: Input handling: detect holding two keys
Post by: Glocke on October 09, 2014, 04:39:35 pm
If your system works as I read it, I wouldn't have thought that clocks would be necessary.
Assuming you're still using move.x and move.y, you could reset them both to zero before the checking and then add to it for each key. That way, if you press up and left, it become -1, -1, and if you press right and left together, x becomes zero.

Well, the problem is, that the system is faster at detecting key presses as the user could press. So the "seems to be simultanously" pressed keys for a diagonal move are not always detected as one action. Mapping keys to a movement vector is the right thing - but the system needs to detect both keys within the same frame as pressed.

Meanwhile, my input component is delaying some milliseconds (currently something between 70-100 I guess) after a single direction key was pressed. If the system recognizes another direction key (which isn't inverse to the previous) it modifies the movement vector and triggers the actual movement to the gameobject. Else (if no additional key occures within the next milliseconds, the previously generated vector (for the single key) is processed.
Title: Re: Input handling: detect holding two keys
Post by: Hapax on October 09, 2014, 08:36:41 pm
Ah, I was incorrect. Don't reset each cycle. Instead just reverse the addition on a release (obviously, you've have to ignore any repeats).

If it starts off at (0, 0) at the beginning:
"Right" event is found; adds 1 to x: (1, 0).
"Up" event is found; subtracts 1 from y: (1, -1).
"Right release" event is found; subtracts 1 from x: (0, -1).
Title: Re: Input handling: detect holding two keys
Post by: Glocke on October 10, 2014, 09:18:00 am
Ah, I was incorrect. Don't reset each cycle. Instead just reverse the addition on a release (obviously, you've have to ignore any repeats).

If it starts off at (0, 0) at the beginning:
"Right" event is found; adds 1 to x: (1, 0).
"Up" event is found; subtracts 1 from y: (1, -1).
"Right release" event is found; subtracts 1 from x: (0, -1).

 ;) That's the way :)