views:

81

answers:

3

I'm developing a small 2D game engine in Java, after playing around with my demo game in a VirtualBox VM hosting Ubuntu, I found a strange bug that would sometimes cause the game to ignore the fact that a key is pressed. So you're running to the left until you suddenly stop moving.

Now under a real Ubuntu I found the cause of the problem. When I hold a key the keyPress/keyRelease events are send all the time.

My system to check for pressed keys is the following:
- if a key gets pressed add it to the "downlist"
- if a key is released add it to the uplist
- on each frame of the game remove the keys in the uplist from the downlist
- if a key is still in the downlist it's pressed

Now when you press a second key, sometimes keyRelease was the last event fired by the other key which is still held but not recognized in that way.

Any ideas how to fix this? It's really annoying.

EDIT
For clarification this is the result I get when holding down a key continuously:
pressed: 87
released: 87
released: 87
pressed: 87
released: 87
pressed: 87
released: 87
pressed: 87
released: 87
etc.

EDIT2
Ok after googling a bit more I found out that this is a "feature" of the X11 server, but I still have no clue how to detect the "fake" key events in java.

+1  A: 

You may be getting some conflicts because of the way you're resolving keypresses every frame and switching between the lists. It may be a bit cleaner to have a boolean for each of the keys you plan to press (such as right arrow, left arrow...etc). When a key is pressed, set the corresponding boolean to true, then when it's released, set it to false. This is a pretty common way to deal with keyboard control in video games

bkritzer
Initially I had it working like that. But then I needed to check if a key was just pressed once(so if you hold it down it will only trigger once) so I came up with the new code which handled that( http://github.com/BonsaiDen/Bonsai-Game-Library/blob/master/src/org/bonsai/dev/GameInput.java), well if there is no way to get rid of that strange GTK behavior I'll have to rewrite the whole keyboard handling from scratch. Also there is still the problem that the game might check just after one of those unnecessary keyReleased events.
Ivo Wetzel
It might be easier to just put the key actions on a timer instead of basing it on the frame timer.
bkritzer
A: 

Okay... I "fixed" it.

Since X11 keeps firing it's auto repeat key events, there's no way to change the whole thing in a way that those "fake" events are ignored, you can't distinguish between "real" and "fake" events in Java.

So the way to fix it is the following. Since every "fake" keyUp event is followed by an immediate keyDown event, you simply remove the keyUp event from the keyRemoveList if you receive a keyDown event this looks like the following:

public final void keyPressed(final KeyEvent e) {
    int key = e.getKeyCode();

    // Fix AutoKeyRepeat under X11
    if (keysRemove.contains(key)) {
        keysRemove.remove(Integer.valueOf(key));
    }

    if (!keysDown.contains(key)) {
        keysDown.add(key);
        keysPressed.add(key);
        lastKeys.add(key);
        if (lastKeys.size() > 16) {
            lastKeys.remove(0);
        }
    }
    e.consume();
}

Since the "real" keyUp event isn't followed by an immediate keyDown event it's processed normally. In theory it should be impossible for a game frame to occur between a "fake" keyUp and keyDown.

Ivo Wetzel
A: 

Of course autorepeat is configurable in X11. Just take a look at xset command option r or -r. You can disable autorepeat for some keycodes using

$ xset -r keycode
dtmilano
Well but that's not really "portable" I can't disable it from Java so every one who plays the game and experiences these issues would have to disable it on his own.
Ivo Wetzel