【问题标题】:keyPressed event slow on first repeatkeyPressed 事件在第一次重复时变慢
【发布时间】:2012-10-24 04:55:15
【问题描述】:

好的,我很抱歉这是一个非常奇怪的问题,但它让我发疯了。

我通过以下方式处理我的游戏的 WASD 移动:

Action ClassWASDKeyPressed = new ClassWASDKeyPressed();
Action ClassWASDKeyReleased = new ClassWASDKeyReleased();
BitKeys movementBitKeys = new BitKeys(); //for WASD movement key pressed/releases

//pressed
theDesktop.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("W"), "wButtonPress");
theDesktop.getActionMap().put("wButtonPress", ClassWASDKeyPressed);
theDesktop.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("A"), "aButtonPress");
theDesktop.getActionMap().put("aButtonPress", ClassWASDKeyPressed);
theDesktop.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("S"), "sButtonPress");
theDesktop.getActionMap().put("sButtonPress", ClassWASDKeyPressed);
theDesktop.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("D"), "dButtonPress");
theDesktop.getActionMap().put("dButtonPress", ClassWASDKeyPressed);
//released
theDesktop.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released W"), "wButtonRelease");
theDesktop.getActionMap().put("wButtonRelease", ClassWASDKeyReleased);
theDesktop.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released A"), "aButtonRelease");
theDesktop.getActionMap().put("aButtonRelease", ClassWASDKeyReleased);
theDesktop.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released S"), "sButtonRelease");
theDesktop.getActionMap().put("sButtonRelease", ClassWASDKeyReleased);
theDesktop.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("released D"), "dButtonRelease");
theDesktop.getActionMap().put("dButtonRelease", ClassWASDKeyReleased);

这是两个 Action 类:

class ClassWASDKeyPressed extends AbstractAction {
    private static final long serialVersionUID = 1L;

    public void actionPerformed(ActionEvent e) {
        if (chatTextField.isFocusOwner() == false){
            if (e.getActionCommand().equals("w")){
                keyPressed(87);
            } else if(e.getActionCommand().equals("a")){
                keyPressed(65);
            } else if(e.getActionCommand().equals("s")){
                keyPressed(83);
            } else if(e.getActionCommand().equals("d")){
                keyPressed(68);
            }
        }
    }
}

class ClassWASDKeyReleased extends AbstractAction {
    private static final long serialVersionUID = 1L;

    public void actionPerformed(ActionEvent e) {
        if (chatTextField.isFocusOwner() == false){
            if (e.getActionCommand().equals("w")){
                keyReleased(87);
            } else if(e.getActionCommand().equals("a")){
                keyReleased(65);
            } else if(e.getActionCommand().equals("s")){
                keyReleased(83);
            } else if(e.getActionCommand().equals("d")){
                keyReleased(68);
            }
        }
    }
}

下面是这个的逻辑:

public void keyPressed(int e) {
    long endTime = System.nanoTime();
    long elapsedTime = endTime - startTime;
    double elapsedTimeSeconds = (double)elapsedTime / 1000000000.0;

    if (elapsedTimeSeconds < .125){ //let them move 8 times a second
        logger.info("KeyPressed (QUICK): " + elapsedTimeSeconds);
    } else {
        logger.info("KeyPressed (VALID): " + elapsedTimeSeconds);
        //logger.debug("Key Pressed: " + e.getKeyChar());  //FOR TROUBLESHOOTING
        movementBitKeys.keyPressed(e);
        //movementBitKeys.showKeyList();  //FOR TROUBLESHOOTING

        if (movementBitKeys.isKeyPressed(87) && !movementBitKeys.isKeyPressed(68) && !movementBitKeys.isKeyPressed(83) && !movementBitKeys.isKeyPressed(65)){
            requestCharacterMove("North");
        }
        if (movementBitKeys.isKeyPressed(87) && movementBitKeys.isKeyPressed(68) && !movementBitKeys.isKeyPressed(83) && !movementBitKeys.isKeyPressed(65)){
            requestCharacterMove("NorthEast");
        }
        if (movementBitKeys.isKeyPressed(87) && !movementBitKeys.isKeyPressed(68) && !movementBitKeys.isKeyPressed(83) && movementBitKeys.isKeyPressed(65)){
            requestCharacterMove("NorthWest");
        }
        if (!movementBitKeys.isKeyPressed(87) && movementBitKeys.isKeyPressed(68) && !movementBitKeys.isKeyPressed(83) && !movementBitKeys.isKeyPressed(65)){
            requestCharacterMove("East");
        }
        if (!movementBitKeys.isKeyPressed(87) && !movementBitKeys.isKeyPressed(68) && movementBitKeys.isKeyPressed(83) && !movementBitKeys.isKeyPressed(65)){
            requestCharacterMove("South");
        }
        if (!movementBitKeys.isKeyPressed(87) && movementBitKeys.isKeyPressed(68) && movementBitKeys.isKeyPressed(83) && !movementBitKeys.isKeyPressed(65)){
            requestCharacterMove("SouthEast");
        }
        if (!movementBitKeys.isKeyPressed(87) && !movementBitKeys.isKeyPressed(68) && movementBitKeys.isKeyPressed(83) && movementBitKeys.isKeyPressed(65)){
            requestCharacterMove("SouthWest");
        }
        if (!movementBitKeys.isKeyPressed(87) && !movementBitKeys.isKeyPressed(68) && !movementBitKeys.isKeyPressed(83) && movementBitKeys.isKeyPressed(65)){
            requestCharacterMove("West");
        }
        startTime = endTime;
    }
}

public void keyReleased(int e) {
    //logger.debug("Key Released: " + e.getKeyChar());  //FOR TROUBLESHOOTING
    movementBitKeys.keyReleased(e);
    //movementBitKeys.showKeyList();  //FOR TROUBLESHOOTING
}

public void keyTyped(int e) {
    // not used - but in case I ever want it
}

还有 BitSet 类:

package com.jayavon.game.helper;

import java.util.BitSet;

public class BitKeys{

    private BitSet keyBits = new BitSet(256);

    public void keyPressed(final int keyCode) {
        keyBits.set(keyCode);
    }

    public void keyReleased(final int keyCode) {
        keyBits.clear(keyCode);
    }

    public void keyTyped(final int keyCode) {
        // don't care
    }

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

    public void showKeyList(){
        System.out.println(keyBits.toString());
    }

}

我的问题是,当您在第一个和第二个“移动”之间按住一个移动键时,比所需的 0.125 毫秒等待时间长得多。以下是一些示例数据:

第一组:

6059 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - 按键(有效):2.567790275

6620 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - 按键(有效):0.560479937

6670 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - 按键(快速):0.0504469 6710 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - 按键(快速):0.09360516 6750 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - 按键(有效):0.129943376

6791 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - 按键(快速):0.04009505 6821 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - 按键(快速):0.07098997 6851 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - 按键(快速):0.102378686 6902 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - 按键(有效):0.152006677

第 2 组:

9690 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - 按键(有效):2.03802468

10272 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - KeyPressed(有效):0.582025645

10322 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - 按键(快速):0.054749323 10342 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - 按键(快速):0.069890042 10372 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - 按键(快速):0.100790212 10412 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - 按键(有效):0.141337411

10462 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - 按键(快速):0.049483458 10462 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - 按键(快速):0.049720381 10512 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - 按键(快速):0.098888524 10542 [AWT-EventQueue-0] 信息 com.jayavon.game.client.MyClient - 按键(有效):0.128729361

现在是时候提问了。显然,您第一次移动经过的时间取决于多长时间,所以这是有道理的。我不明白为什么第二个“运动”大约是 0.5 而不是更接近 0.125。从数据中可以看出,第三步和第四步在按住键时触发得非常快,以至于它们小于 0.125。

谁能帮助我解决这个问题?任何以任何方式改进我的代码的建议都会有很大的帮助。毫秒计数。

【问题讨论】:

标签: java events keypress bitset


【解决方案1】:

在@aioobe 的大力帮助下,我解决了这个问题。

Timer timer = new Timer();
timer.schedule(new TimerTask() {
    public void run() {
        checkKeyStrokes();
    }
}, 0, 0135);

public void checkKeyStrokes(){
    if (movementBitKeys.isKeyPressed(87)){ //w
        keyPressed(87);
    }
    if (movementBitKeys.isKeyPressed(65)){ //a
        keyPressed(65);
    }
    if (movementBitKeys.isKeyPressed(83)){ //s
        keyPressed(83);
    }
    if (movementBitKeys.isKeyPressed(68)){ //d
        keyPressed(68);
    }
}

希望这对其他人有帮助!!!

【讨论】:

    【解决方案2】:

    分析您的代码后 - 我的快速回复

    • 当一个键被按下来快速它不被你的motionBitKeys识别(无效) 这可能是个问题。当您快速按下按键时,例如“s”后跟“w”而不是“a”。

    "s" 有效(向南移动)- 向南移动

    (释放“s” - 什么都不做)s

    “w”无效(快速)

    “a”有效(向东移动)->向东移动->这是错误的-应该是东北

    我的另一个建议。 目前您使用两个计时器。一个用于 elapsedTimeInSeconds,一个 Timer 用于控制按下的键。

    这可以在一个过程中完成

    class ClassWASDKeyPressed extends AbstractAction {
      private static final long serialVersionUID = 1L;
    
      public void actionPerformed(ActionEvent e) {
        if (chatTextField.isFocusOwner() == false){
            if (e.getActionCommand().equals("w")){
                movementBitKeys.keyPressed(87);
            } else if(e.getActionCommand().equals("a")){
                movementBitKeys.keyPressed(65);
            } else if(e.getActionCommand().equals("s")){
                movementBitKeys.keyPressed(83);
            } else if(e.getActionCommand().equals("d")){
                movementBitKeys.keyPressed(68);
            }
        }
      }
    

    }

    还有 TimerTask 的变化

    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
      public void run() {
        move(movementBitKeys); // instead of keypressed - without elapsedTimeSeconds  
      }
    }, 0, 0125);
    

    【讨论】:

    • 谢谢你。实际上,前几天我自己发现了它,当时它工作不正常,这就是我修复它的方法。我打算回到这里添加它 :) 好提醒:if (elapsedTimeSeconds
    • 直到现在我还没有阅读/理解您的确切意思,您的想法实际上有助于简化很多事情。非常感谢......我会给你赏金:)
    【解决方案3】:

    如果您按住某个键,则在发送重复事件之前会有一个计时器等待。这听起来与Eliminating Initial keypress delayFixing delay issue in java keypress action 此处发布的问题非常相似

    为什么不直接假设当您收到 keyPressed 事件时,该键已按下,直到您获得 keyReleased。因此,不要依赖系统为您生成事件。拥有自己的计时器,当您收到 keyPressed 事件并且没有 keyRelease 时,它​​会在您要求的时间间隔内请求角色沿按键方向移动。当您收到 keyRelease 时,只需停止计时器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多