1
\$\begingroup\$

Determining if a key is currently down is easy. Just return keys[scancode]:

switch (ev.type) {
    case SDL_KEYDOWN:
        keys[ev.key.keysym.scancode] = true;
        break;
    case SDL_KEYUP:
        keys[ev.key.keysym.scancode] = false;
        break;
}

Determining if a key was pressed or released this frame is another thing. I just don't know where to start here. It would be accessed by keyPressed(scancode) / keyReleased(scancode), what's a good way to do this?

Of course it would also be easy to do if I just handled it inside of the event handler itself but I'm just using functions anything can call to check the keys.

\$\endgroup\$
4
\$\begingroup\$

Store the keys from the last frame and compare in the current frame.

if(!old_keys[key] && keys[key]) { /* pressed since last frame */ } 
if(old_keys[key] && !keys[key]) { /* released since last frame */ } 
if(old_keys[key] && keys[key]) { /* held since last frame */ }
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Wouldn't this give a 1-frame input lag? \$\endgroup\$ – Accumulator Jul 26 '15 at 3:23
  • \$\begingroup\$ It would, but unless you're running at a very low framerate, it's not going to be noticeable. If you need better than that, you should look into getting input in a separate thread with a higher polling rate, but at that point you should consider if the gains are worth the effort. \$\endgroup\$ – William Mariager Jul 26 '15 at 3:26
  • \$\begingroup\$ Would you perhaps know if the NES/SNES did it like this? I'm trying to create a C++ engine that's as close to NES/SNES as possible. \$\endgroup\$ – Accumulator Jul 26 '15 at 3:28
  • \$\begingroup\$ I could only guess. But I doubt it'll make a noticeable difference. As I recall from my childhood playing NES/SNES, I rarely tried to press more often than the framerate on screen. \$\endgroup\$ – William Mariager Jul 26 '15 at 3:40
  • 3
    \$\begingroup\$ Actually, there would be no input lag with the above solution. The events are still happening against the current key state. You are just comparing with the previous state to see if the key was released or held. What would be true is that a key press AND release cannot happen in the same frame. So if you are running at 30fps, you can detect 15 key presses, 15 key ups and 30 key holds per second, assuming you need all 3 event types. \$\endgroup\$ – Fuzzy Logic Jul 26 '15 at 5:22
0
\$\begingroup\$

If you have to handle a large set of keys have a look at:

SDL_GetKeyState

which gives you a snapshot of the state of the whole keyboard.

| improve this answer | |
\$\endgroup\$
-1
\$\begingroup\$

Instead of true/false I use a value of 2/-2 for a recent change (2 for a new press, -2 for a new release), then at the beginning of the loop, before going through the new events I go through the whole array and change every 2 or -2 to 1 or -1. I use 3 in case of a key repeat, so I can handle all those different things in the rest of my code smoothly. So if I test the key for ==2 it means only new presses, if I test for >=2 it means new presses and repeats, >=1 is if it's down at all and so on.

But if you don't want to directly use numbers (probably best not to if other people need to understand your code) you can use the following enum:

enum
{
    KEYUP_NEW = -2,
    KEYUP,
    KEY_UNINIT,
    KEYDOWN,
    KEYDOWN_NEW,
    KEYDOWN_REPEAT
};

and then you only have to test for if (keys[key]==KEYDOWN_NEW) instead of the less readable if (!old_keys[key] && keys[key]) whose meaning isn't as instantly obvious. You can even go one better by having a function do the test for you so that test_key(key, KEYDOWN) or is_key_down(key) can return positive whether the value is KEYDOWN, KEYDOWN_NEW or KEYDOWN_REPEAT if you don't want to use the less obvious if (keys[key]>=KEYDOWN).

But there's a big problem with your approach, it's that you'll miss keypresses that last shorter than your average loop because in those cases you'll have a down event instantly followed by a up event, and therefore your table as seen from outside of the event polling loop will miss the keypress entirely. I have a solution for this, it's to have another table and when you detect a down event while your entry in your table is 2 then don't change it to -2 but rather set the matching entry in that other table to 1. This way for that one iteration of the loop your key is set to 2, which is what you want. Then when the next loop starts, at the same time as you turn 2s into 1s you check that other table and if it's set to 1 then you turn the 2 into a -2 so that you signal the release.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Why are you downvoting me, I'm right! \$\endgroup\$ – Michel Rouzic Nov 24 '18 at 9:20
  • 1
    \$\begingroup\$ I'm not the downvoter, but using magic numbers doesn't seem like a very great solution. \$\endgroup\$ – Tyyppi_77 Nov 24 '18 at 15:52
  • \$\begingroup\$ I'm not the downvoter either but what I'm wondering is why you even post an answer to a question which clearly already has a very good, and also accepted, answer posted. And also, like Tyyppi_77 has written - magic number usage. \$\endgroup\$ – Daniel_1985 Nov 25 '18 at 1:59
  • \$\begingroup\$ As my second paragraph points out the other answers have a flaw (very significant at low FPS and even worse if you use remote desktoping which can send the key release event without delay after the key press event), and having to test for only one thing feels more elegant to me. You can always use an enum and have something like KEYUP_NEW, KEYUP, KEYDOWN, KEYDOWN_NEW, KEYDOWN_REPEAT. I have an old habit of using those numbers for all sorts of flags so it doesn't look weird to me. if (keys[key]==KEYDOWN_NEW) seems more clear than if(!old_keys[key] && keys[key]) to me. \$\endgroup\$ – Michel Rouzic Nov 25 '18 at 8:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.