处理多次按键忽略重复键

我在另一个问题的评论部分询问了这个问题(> 我如何处理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的方法都会很棒,因为我很紧张我使用了太多类型的“简单”。

您最近很可能会缓慢处理事情的观点仅仅是由许多System.out.println()语句引起的。

你没有得到对角线运动的问题源于你有点错误的检查逻辑 – 而不是明确地检查是否(例如)键A B被按下,只需单独检查它们 – 键A在一个方向上移动字符,在另一个方向上移动B 。 总共(例如),通过移动WEST NORTH,您将有效地移动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类记录密钥的代码量减少到几行:)

作为替代方案,该游戏使用数字小键盘通过单次击键实现每个(半)基本方向。 默认排列显示在“ 设计”部分中。 可以单独重新分配键以在键盘上的任何地方映射类似的莲座。

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

每当你得到一个事件时,它就会被一个特殊的线程传递给你(我相信它是事件派遣线程,但除此之外)。 您不允许在此线程上执行任何操作(这需要时间),这会冻结您的输入并导致您错过事件,从而使Java看起来没有响应。 所以发生的事情是你在java中破坏了事件系统。 你应该做的是将结果存储在某种缓冲区中,这是你可以期望对事件做的事情,然后按照我的描述稍后处理。

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

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