KeyListener - 按住时忽略键释放(Ubuntu,...)

时间:2013-05-15 20:34:24

标签: java awt keylistener

我有一个你可以攻击的游戏。当按下SPACE键时,武器会向下移动,当它被释放时它会向上移动。

问题在于(在某些)UNIX系统上,KeyReleased事件也会在按住键的同时触发。我正在使用BitSet跟踪按下哪些键,但由于KeyReleased持续发射,武器只能在我的Ubuntu机器上快速上下移动。

我可以同时移动我的两个角色,但是在按下空格键时我无法保持武器。

(按住键几秒后会发生什么事情: keyPressed - keyReleased - keyPressed - keyReleased - keyPressed - keyReleased - ....)

这是一个System.out,会发生什么,在每个动作之前使用System.currentTimeMillis():

1368696607559 - Pressed
1368696608052 - Released
1368696608053 - Pressed
1368696608082 - Released
1368696608083 - Pressed
1368696608112 - Released
1368696608113 - Pressed
1368696608143 - Released
1368696608144 - Pressed
1368696608173 - Released

我正在使用的代码是:

//Inner Class
private class KeyDispatcher extends KeyAdapter implements ActionListener{

    private BitSet keyBits = new BitSet(256);
    private Timer timer = new Timer(20, this);
    boolean player1Attack = false, player2Attack = false;

    public KeyDispatcher(){
        timer.start();
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        keyBits.set(keyCode);
    }

    @Override
    public void keyReleased(KeyEvent e) {
        int keyCode = e.getKeyCode();
        keyBits.clear(keyCode);
        if(player1Attack && !isKeyPressed(KeyEvent.VK_SPACE)){
            controller.stopAttack(0);
            player1Attack = false;
        }
        //player 2
        ...
    }

    public boolean isKeyPressed(final int keyCode) {
        return keyBits.get(keyCode);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if(isGameOver()){
            timer.stop();
        }else{
            //player1
            if(!player1Attack && isKeyPressed(KeyEvent.VK_RIGHT)){
                controller.moveRight(0);
            }
            if(!player1Attack && isKeyPressed(KeyEvent.VK_UP)){
                controller.moveUp(0);
            }
            if(!player1Attack && isKeyPressed(KeyEvent.VK_DOWN)){
                controller.moveDown(0);
            }
            if(!player1Attack && isKeyPressed(KeyEvent.VK_LEFT)){
                controller.moveLeft(0);
            }
            if(!player1Attack && isKeyPressed(KeyEvent.VK_NUMPAD0)){
                controller.attack(0); player1Attack = true; update();
            }
            //player2
            ...
        }
    }

}

我也尝试用KeyBinding替换它,因为这些似乎更值得推荐,但是这里仍然存在相同的“问题”(释放的动作在按下时会一直触发)......

    Action attack = new AbstractAction(){

        @Override
        public void actionPerformed(ActionEvent e) {
            controller.attack(0);
        }

    };
    Action stopAttack = new AbstractAction(){

        @Override
        public void actionPerformed(ActionEvent e) {
            controller.stopAttack(0);
        }

    };
    background.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "attack");
    background.getActionMap().put("attack", attack);

    background.getInputMap().put(KeyStroke.getKeyStroke("released SPACE"), "stopAttack");
    background.getActionMap().put("stopAttack", stopAttack);

我已经阅读了有关这种情况的多个主题,但未能使其正常运行。

提前致谢

3 个答案:

答案 0 :(得分:3)

让我们举一个例子,在其中一台UBUNTU机器上遇到问题; 假设他们按住键盘按钮1000毫秒。

根据你的评论,这就是时间线上会发生的事情:

0 ms         -    10ms         -   1000ms
keyPressed      keyReleased        keyReleased

因此,在这些有问题的计算机中,您有1x keyPressed事件和2x keyReleased事件。 第二个关键发布的事件,是坏人。

我会执行以下操作来“修补”问题(直到您正确识别这些UBUNTU计算机中的差异):

(注意我使用的是系统时间,而不是每秒固定的MS,因为KeyEvents发生在UI线程而不是游戏或OpenGL线程上)

//This isn't the 10ms of the example, but if this doesn't work, try increasing it a bit further or 
//try meassuring the exact time of the "faulty/not wanted" first onKeyReleased event  
final private static long KEY_RELEASE_TIME_LOCK_IN_MS = 100L;
private static long lastKeyRelease_TimeMillis;

@Override
public void keyPressed(KeyEvent e) {

    int keyCode = e.getKeyCode();
    keyBits.set(keyCode);


}



@Override
public void keyReleased(KeyEvent e) {
    final long currentTimeMillis = System.currentTimeMillis();

    if( (currentTimeMillis - lastKeyRelease_TimeMillis) <= KEY_RELEASE_TIME_LOCK_IN_MS){
        //We just ignored the keyRelease!, we are not allowing keyReleases that fast!
        return;
    }


    int keyCode = e.getKeyCode();
    keyBits.clear(keyCode);
    if(player1Attack && !isKeyPressed(KeyEvent.VK_SPACE)){
        controller.stopAttack(0);
        player1Attack = false;
    }

    lastKeyRelease_TimeMillis = System.currentTimeMillis();

    //player 2
    ...
}


//Rockster., let me know if it worked out

答案 1 :(得分:0)

我有点跟踪Rockster建议的时间。因为在keyReleased之后几乎立即触发了keyPressed,你可以检查:      keyPressedTime

当应用程序繁忙时(在我的情况下将敌人添加到字段中),这有时会失败,因为keyPressed不会立即触发。但对于这个应用程序来说,这不是什么大问题。

感兴趣的代码:

//Inner Class
private class KeyDispatcher extends KeyAdapter implements ActionListener{

    private long p1AttackLastKeyRelease = 0, p1AttackLastKeyPressed = 0;
    private BitSet keyBits = new BitSet(256);
    private Timer timer = new Timer(20, this);
    boolean player1Attack = false;

    public KeyDispatcher(){
        timer.start();
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        if(keyCode == KeyEvent.VK_NUMPAD0){
            p1AttackLastKeyPressed = System.currentTimeMillis();
        }
        keyBits.set(keyCode);
    }

    @Override
    public void keyReleased(KeyEvent e) {
        int keyCode = e.getKeyCode();
        if(keyCode == KeyEvent.VK_NUMPAD0){
            p1AttackLastKeyRelease = System.currentTimeMillis();
        }
        keyBits.clear(keyCode);
    }

    public boolean isKeyPressed(final int keyCode) {
        return keyBits.get(keyCode);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if(isGameOver()){
            timer.stop();
        }else{
            //release the key
            if(p1AttackLastKeyPressed != 0 && p1AttackLastKeyPressed<p1AttackLastKeyRelease && player1Attack && !isKeyPressed(KeyEvent.VK_NUMPAD0)){
                p1AttackLastKeyPressed = 0;
                controller.stopAttack(0);
                player1Attack = false;
            }
            //do the actions of the keys that are down
            if(!player1Attack && isKeyPressed(KeyEvent.VK_RIGHT)){
                controller.moveRight(0);
            }
            if(!player1Attack && isKeyPressed(KeyEvent.VK_UP)){
                controller.moveUp(0);
            }
            if(!player1Attack && isKeyPressed(KeyEvent.VK_DOWN)){
                controller.moveDown(0);
            }
            if(!player1Attack && isKeyPressed(KeyEvent.VK_LEFT)){
                controller.moveLeft(0);
            }
            if(!player1Attack && isKeyPressed(KeyEvent.VK_NUMPAD0) && !isKeyPressed(KeyEvent.VK_UP) && !isKeyPressed(KeyEvent.VK_DOWN) && !isKeyPressed(KeyEvent.VK_LEFT) && !isKeyPressed(KeyEvent.VK_RIGHT)){
                controller.attack(0); player1Attack = true; update();
            }
        }
    }

}

这仍然没有像我之前说过的那样完全正常,但对于这个应用来说已经足够好了(玩家无法从中获得优势)。

答案 2 :(得分:0)

您应该尝试使用Key Bindings。

More information from docs here