KeyListener 事件的短暂延迟

Short delay for KeyListener events

我正在编写一个小游戏。到目前为止,我有一个玩家,他可以跟随玩家在 2D 打印的瓷砖世界中移动。这里有个小的preview.

现在我的玩家移动代码如下所示:

KeyListener class

public void keyPressed(KeyEvent e) {
    int key = e.getKeyCode();
    for(int i = 0; i < handler.object.size(); i++){
        GameObject tempObject = handler.object.get(i);

        if(tempObject.getId() == ID.Player){
            if(key == KeyEvent.VK_D) tempObject.setVelX(5);
            if(key == KeyEvent.VK_A) tempObject.setVelX(-5);
            if(key == KeyEvent.VK_SPACE && Var.jump == true) {
                tempObject.setVelY(-10);
                Var.jump = false;
            }
        }
    }
public void keyReleased(KeyEvent e) {
    int key = e.getKeyCode();

    for(int i = 0; i < handler.object.size(); i++){
        GameObject tempObject = handler.object.get(i);

        if(tempObject.getId() == ID.Player){
            if(key == KeyEvent.VK_D) tempObject.setVelX(0);
            if(key == KeyEvent.VK_A) tempObject.setVelX(0);
        }
    }

玩家class

public void tick() {
    if (Var.falling == true) {
        velY += Var.gravity;
        if (velY > 10) {
            velY = 10;
        }
    }
    x += velX;
    y += velY;

这些代码只是 classes 的一部分。

问题

当我用 A 或 D 向右或向左移动,然后以某种方式点击另一个键(从 a 到 d/从 d 到 a)时,我的角色会静止一小会儿然后在短暂的延迟后移动。参见 here

  1. 用户按下 D,触发对 keyPressed 的调用,后者调用 tempObject.setVelX(5).
  2. 用户释放 A,这会触发对 keyReleased 的调用,后者会调用 tempObject.setVelX(0)。这会停止你角色的动作。
  3. 用户仍在按住 D,因此在短暂的延迟后,该键的自动重复触发了对 keyPressed 的另一次调用,这导致 tempObject.setVelX(5)再次调用,导致角色再次开始移动。

除非没有按下移动键,否则你不应该调用tempObject.setVelX(0)

你怎么确定的?您需要定义跟踪它们的实例字段。例如:

private boolean leftPressed;
private boolean rightPressed;

// ...

public void keyPressed(KeyEvent e) {
    //...

    if (key == KeyEvent.VK_D) {
        rightPressed = true;
        tempObject.setVelX(5);
    }
    if (key == KeyEvent.VK_A) {
        leftPressed = true;
        tempObject.setVelX(-5);
    }

    //...
}

public void keyReleased(KeyEvent e) {
    //...

    if (key == KeyEvent.VK_D) {
        rightPressed = false;
        if (!leftPressed) {
            tempObject.setVelX(0);
        }
    }
    if (key == KeyEvent.VK_A) {
        leftPressed = false;
        if (!rightPressed) {
            tempObject.setVelX(0);
        }
    }

    //...
}

一些旁注:

  • gameObject 是比 tempObject.
  • 更好的变量名
  • 你永远不应该写“booleanVariable == true”。相反,只需编写 booleanVariable。通过写一个“=”而不是两个来避免意外赋值的任何可能性。例如:
    if (Var.falling) {

更简单地说:从您的代码中删除所有出现的 == true

如果不知道 keyups 和 keydowns 的确切顺序,很难说出确切的问题是什么,但看起来您的程序容易受到重叠 keyup/keydown 序列的影响 - 例如:

A down -> velocity = -5
D down -> velocity = +5
A up   -> velocity = 0 // but you want it to still be +5

将日志记录添加到您的游戏代码以帮助调试这些情况。

有多种方法可以解决这个问题,但一种方法是保持 运行 当前按下键的计数:

public void keyPressed(KeyEvent e) {
     heldKeys++;
     ... other code ...
}

public void keyReleased(KeyEvent e) {
     heldKeys--;
     ... other code ...
     if(heldKeys == 0) {
          velocity = 0;
     }
}

这是轻巧但生硬的。更精确的方法可能是记录每个键的状态:

public void keyPressed(KeyEvent e) {
    int key = e.getKeyCode();
    heldKeys.set(key, true);
}

...根据 keyStates.

中的内容更新您的游戏状态

根据您对效率的狂热程度,keyStates 可能是 HashSet(Integer, Boolean) 或您自己创造的更专业的东西。


但是!有关从键盘自动重复的讨论,请参阅 How to know when a user has really released a key in Java?