我在另一个问题的评论部分问过这个问题(> 如何在 Java 中处理同时按键?),并被要求提出一个新问题。
我的问题是,当我创建按键的 ArrayList 时,如果用户按住按键,则通过 keyRelease 事件删除它们的速度不够快。 我希望运动与"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) 变量添加第二次签入之前,创建的金字塔是巨大的。 即使有第二次检查,列表也会增长,几乎总是有两到三个重复项。 这方面的任何帮助都会很棒,因为我的角色移动得很笨拙。:(
此外,任何删除重复转换为字符、字符串、arrayList 的方法都会很棒,因为我很紧张,我为"简单"的东西使用了太多类型。
你对事情处理缓慢的痴迷很可能仅仅是由于许多System.out.println()语句造成的。
您没有获得对角线移动的问题源于您有点错误的检查逻辑 - 而不是明确检查(例如)是否按下了 A 和 B 键,只需独立检查它们 - 键 A 将字符向一个方向移动,B 在另一个方向上移动。总的来说(例如),通过移动西和北,您将有效地向西北移动。
您可以使用java.util.BitSet,只需为当前按下的每个键设置位,而不是按下的键列表。这也应该大大减少您需要编写的代码量(keyPress仅设置键代码指示的位,keyRelease清除它)。要检查是否按下了某个键,您需要询问 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 将冻结,直到延迟结束!
您应该在侧面运行不同的线程(可能是您的主线程)。它将运行某种循环,控制程序中的帧,每个游戏周期完成一次。每个循环后,此线程将读取存储在输入缓冲区中的结果并对其进行处理。这背后的理论很简单,但执行可能有点混乱,因为您需要确保没有输入事件被删除或读取更多次。无论哪种方式,祝您的游戏好运!