Java keyPress/keyRelease issue under Linux/X11
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 pr开发者_运维知识库essed 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 pressedNow 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.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
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.
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
精彩评论