1

我在另一个问题的评论部分提出了这个问题(>如何在 Java 中处理同时按下的按键?),并被要求完全提出一个新问题。

我的问题是,当我创建按键的 ArrayList 时,如果用户按住按键,它们不会通过 keyReleased 事件足够快地删除。我希望运动与“asdf”和北,东,南,西,东北......等一起。

这是我的两个事件的代码:

@Override
public void keyPressed(KeyEvent e) {
    if(chatTextField.isFocusOwner() == true){
        //do nothing - don't walk
    } else {
        logger.debug("Key Pressed: " + e.getKeyChar());
        lastKey = keysPressed.get(keysPressed.size()-1);

        for (String key : keysPressed){
            if (!key.contains(String.valueOf(e.getKeyChar())) && !lastKey.contains(String.valueOf(e.getKeyChar()))){
                keysPressed.add(String.valueOf(e.getKeyChar()));
                System.out.println("ADDED: " + keysPressed);
            }
        }

        String keysList = keysPressed.toString();
        if (keysList.contains("w")){
            if (keysList.contains("d")){
                requestCharacterMove("NorthEast");
            } else if(keysList.contains("a")){
                requestCharacterMove("NorthWest");
            } else{
                requestCharacterMove("North");
            }
        } else if (keysList.contains("s")){
            if (keysList.contains("d")){
                requestCharacterMove("SouthEast");
            } else if(keysList.contains("a")){
                requestCharacterMove("SouthWest");
            } else{
                requestCharacterMove("South");
            }
        } else if (keysList.contains("d")){
            requestCharacterMove("East");
        } else if (keysList.contains("a")){
            requestCharacterMove("West");
        }
    }
}

@Override
public void keyReleased(KeyEvent e) {
    if(chatTextField.isFocusOwner() == true){
        //do nothing - don't walk
    } else {
        logger.debug("Key Released: " + e.getKeyChar());
        for (String key : keysPressed){
            if (key.contains(String.valueOf(e.getKeyChar()))){
                keysPressed.remove(String.valueOf(e.getKeyChar()));
                System.out.println("REMOVED: " + keysPressed);
            }
        }
    }
}

@Override
public void keyTyped(KeyEvent arg0) {
    // TODO Auto-generated method stub

}

直到我通过 lastKey(String) 变量在那里添加第二次检查,金字塔创建的规模是巨大的。即使进行第二次检查,列表也会增加,并且几乎总是有两到三个重复项。由于我的角色笨拙地移动,因此对此的任何帮助都会很棒。:(

此外,任何删除对 char、string、arrayList 的重复转换的方法都会很棒,因为我很紧张我为“简单”的东西使用了太多类型。

4

3 回答 3

4

您认为事情处理得很慢很可能是由于许多System.out.println () 语句引起的。

您没有得到对角线移动的问题源于您的检查逻辑有些错误 - 而不是明确检查(例如)键 AB 是否被按下,只需独立检查它们 - 键 A 将字符移动到一个方向,B 移动到另一个方向. 总的来说(例如),通过向 WESTNORTH 移动,您将有效地向 NORTHWEST 移动。

您可以使用 java.util.BitSet 并为当前按下的每个键设置位,而不是按下键的列表。这也应该大大减少您需要编写的代码量(keyPressed 只是设置键代码指示的位,keyReleased 清除它)。要检查是否按下了某个键,请询问 BitSet,然后询问当前是否设置了代码的位。

编辑:使用 BitSet 而不是列表的示例

public class BitKeys implements KeyListener {

    private BitSet keyBits = new BitSet(256);

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

    @Override
    public void keyReleased(final KeyEvent event) {
        int keyCode = event.getKeyCode();
        keyBits.clear(keyCode);
    }

    @Override
    public void keyTyped(final KeyEvent event) {
        // don't care
    }

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

}

我让示例实现了 KeyListener,因此您甚至可以按原样使用它。当您需要知道某个键是否被按下时,只需使用 isKeyPressed()。您需要决定是喜欢使用原始密钥代码(就像我一样)还是使用关键字符(就像您目前所做的那样)。在任何情况下,您都会看到如何使用 BitSet 类来记录键的代码量减少到几行 :)

于 2012-08-07T17:54:32.777 回答
1

作为替代方案,该游戏使用数字键盘通过单次击键来实现每个(半)基本方向。默认排列显示在设计部分。可以单独重新分配按键以在键盘上的任何位置映射类似的玫瑰花结。

于 2012-08-07T21:21:03.577 回答
0

看起来您没有正确处理 Java 中的线程。任何 Java 程序都有三个线程(最少)。它们是主程序线程,事件调度线程,还有一个我现在不记得了。

每当您收到一个事件时,它都会通过一个特殊的线程传递给您(我相信它是事件调度线程,但这不是重点)。你不能在这个线程上做任何事情(这需要时间),这会冻结你的输入并导致你错过事件,使 Java 看起来没有响应。所以发生的事情是你破坏了java中的事件系统。您应该做的是将结果存储在某种缓冲区中,这是您可以期望对事件执行的禁食操作,然后按照我将描述的方式稍后处理。

[旁白:一个有趣的应用程序是制作一个简单的 gui,并在按下按钮调用线程上等待 5 秒钟。您的整个 gui 将冻结,直到延迟结束!]

您应该在旁边运行一个不同的线程(可能是您的主线程)。它将运行某种循环,控制程序中的帧,每个游戏循环完成一次。在每个周期,该线程读取存储在输入缓冲区中的结果并处理它们。这背后的理论很简单,但执行可能有点混乱,因为您需要确保没有输入事件被丢弃或读取一次以上。不管怎样,祝你的游戏好运!

于 2012-11-12T18:16:14.263 回答