2

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

问题是在(某些)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);

我已经阅读了有关这种情况的多个主题,但无法使其正常工作。

提前致谢

4

3 回答 3

3

因此,让我们举一个例子,在您遇到问题的那些 UBUNTU 机器中;假设他们按住键盘按钮 1000 毫秒。

根据您的评论,时间线上会发生以下情况:

0 ms         -    10ms         -   1000ms
keyPressed      keyReleased        keyReleased

因此,在这些有问题的计算机中,您有 1x keyPressed 事件和 2x keyReleased 事件。第二个 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
于 2013-05-15T20:56:52.937 回答
0

您应该尝试使用键绑定。

来自文档的更多信息here

于 2013-08-11T22:10:37.280 回答
0

我得到了一种像 Rockster 建议的那样跟踪时间的方法。因为 keyPressed 在 keyReleased 之后几乎立即被触发,所以您可以检查是否: 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();
            }
        }
    }

}

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

于 2013-05-16T16:11:08.870 回答