简体   繁体   English

处理多次按键而忽略重复按键

[英]handle multiple key presses ignoring repeated key

I had asked this in the comments section of another question (> How do I handle simultaneous key presses in Java? ), and was asked to make a new question altogether. 我在另一个问题(> 如何在Java中同时处理按键? )的注释部分提出了这个问题,并被要求提出一个新问题。

My problem is that when I create an ArrayList of keypresses they are not removed fast enough via the keyReleased event if the user holds down the keys. 我的问题是,当用户按下按键时,当我创建按键列表ArrayList时,无法通过keyReleased事件足够快地将其删除。 I want movement to be with "asdf" and North, East, South, West, NorthEast... etc. 我希望运动与“ asdf”和北,东,南,西,东北...等保持一致。

Here is my code for both events: 这是我的两个事件的代码:

@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

}

Until I added the second check in there via the lastKey(String) variable the pyramid created was enormous. 直到我通过lastKey(String)变量将第二个检查添加到那里,金字塔创建的数量巨大。 Even with that second check the list grows and almost always has two-three duplicates. 即使进行了第二次检查,该列表也会增加,并且几乎总是重复两到三遍。 Any help on this would be great as my character is moving awkwardly. 因为我的角色笨拙地移动着,所以对此的任何帮助都是很棒的。 :( :(

Also any way to remove duplicate conversions to char, string, arrayList would be great as I'm nervous I used too many types for something "simple". 同样,删除重复转换为char,string,arrayList的任何方法也将非常有用,因为我担心我为“简单”使用了太多类型。

Your obseravtion that things are handled slowly most likely is caused solely be the many System.out.println() statements. 你obseravtion事情最有可能慢慢地处理引起是众多的System.out.println()语句。

Your problem that you do not get diagonal movement stems from your somewhat faulty checking logic - instead of explicitly checking if (for example) keys A and B are pressed, just check them independently - key A moves the character in one direction, B in another. 您没有得到对角线移动的问题是由于您的检查逻辑有些错误-而不是明确检查(例如)是否按下了键A B,而是单独检查了它们-键A在一个方向上移动了字符,而B在另一个方向上移动了字符。 In total (eg), by moving WEST and NORTH you will have effectively moved NORTHWEST. 总体而言(例如),通过移动WEST NORTH,您将有效地移动NORTHWEST。

Instead of a list of pressed keys, you could use a java.util.BitSet and just set the bit for each key that is currently pressed. 除了使用按键列表之外,还可以使用java.util.BitSet并为每个当前按下的键设置位。 That should also drastically reduce the amount of code you need to write (keyPressed just sets the bit indicated by key code, keyReleased clears it). 这也应该大大减少您需要编写的代码量(keyPressed只是设置密钥代码指示的位,keyReleased清除该位)。 To check if a key is pressed you ask the BitSet then if the bit for the code is currently set. 要检查是否按下了按键,请询问BitSet,然后询问当前是否设置了代码位。

EDIT: Example of using BitSet instead of a list 编辑:使用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);
    }

}

I made the example implement KeyListener, so you could even use it as is. 我使示例实现了KeyListener,因此您甚至可以按原样使用它。 When you need to know if a key is pressed just use isKeyPressed(). 当您需要知道是否按下某个键时,请使用isKeyPressed()。 You need to decide if you prefer with raw key code (like I did) or go with key character (like you currently do). 您需要确定是使用原始密钥代码(像我一样)还是选择密钥字符(像您当前所做的那样)。 In any case, you see how using the BitSet class the amount of code for recording the keys reduces to a few lines :) 无论如何,您都会看到使用BitSet类将用于记录键的代码量减少到几行:)

As an alternative, this game uses the numeric keypad to implement each (semi-) cardinal direction with a single keystroke. 作为替代方案,该游戏使用数字键盘通过一次击键来实现每个(半)基本方向。 The default arrangement is shown in the Design section. 默认排列显示在“ 设计”部分中。 The keys may be individually reassigned to map a similar rosette anywhere on the keyboard. 可以分别重新分配键,以在键盘上的任何位置映射相似的花环。

Looks like you are not handling threading in Java right. 看起来您没有正确处理Java中的线程。 There are three threads (minimum) to any Java program. 任何Java程序都有三个线程(最少)。 They are the main program thread, the event dispatch thread, and one more that i can't remember right now. 它们是主程序线程,事件分发线程,还有我现在不记得的另一个线程。

Whenever you get an event it is delivered to you by a special thread (I believe it's the event dispatch thread, but that is besides the point). 每当您收到事件时,都会通过特殊线程将事件传递给您(我相信这是事件分发线程,但这并不重要)。 You are not allowed to do anything (that takes time) on this thread, that will freeze up your input and cause you to miss events, making Java look unresponsive. 您不允许在此线程上执行任何操作(这需要时间),这将冻结您的输入并导致您错过事件,从而使Java显得无响应。 So what has happened is you have broke the event system in java. 所以发生的事情是您破坏了Java中的事件系统。 What you should do is store the result in some sort of buffer, which is the fasted thing you can be expected to do with the event, then it is handled later as I will describe. 您应该做的是将结果存储在某种缓冲区中,这是您可以期望对事件进行的禁食,然后稍后再处理,如我将描述的那样。

[Aside: A funny application is to make a simple gui, and on the press of the button call wait on the thread for like 5 seconds. [另外:一个有趣的应用程序是制作一个简单的GUI,并在按下按钮调用时在线程上等待5秒钟。 Your entire gui will freeze until the delay has finished!] 您的整个gui将冻结,直到延迟结束!]

You should have a different thread running on the side (probably your main thread). 您应该在侧面运行其他线程(可能是主线程)。 It will run some sort of loop, which controls the frames in your program, completing once per game cycle. 它将运行某种循环,该循环控制程序中的帧,每个游戏周期完成一次。 Once each cycle this thread reads the results stored in the input buffer and processes them. 每次循环后,该线程都会读取存储在输入缓冲区中的结果并进行处理。 The theory behind this is simple, but the execution can be a little messy, because you will need to make sure that no input events are dropped or read more then once. 其背后的理论很简单,但是执行起来可能有点混乱,因为您将需要确保没有任何输入事件被丢弃或读取的次数超过一次。 Either way, good luck with your game! 无论哪种方式,祝您游戏顺利!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM