如何在Java中创建可用的KeyReleased方法 [英] How to create a usable KeyReleased method in java

查看:105
本文介绍了如何在Java中创建可用的KeyReleased方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是新手,我尝试使正方形移动,但仅当释放键(例如W)时才可,但是当我按住键时,正方形会移动

I'm new to swing and I am trying to make the square move but only when the key is released (ex W) but when i hold down the key the square just moves

KeyListener类

KeyListener Class

我要确保已按下某个键,如果仍然按下该键,则应该返回false,但是如果按下然后释放,则返回true 包javaGD.GameAssistant.Input;

I want to make sure that a key was pressed and if it is still pressed it should return false but true if it was pressed then released package javaGD.GameAssistant.Input;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class KeyManager implements KeyListener {
private boolean[] keys;

public KeyManager() {
    keys = new boolean[256];

}

@Override
public void keyPressed(KeyEvent e) {
    keys[e.getKeyCode()] = true;

}

@Override
public void keyReleased(KeyEvent e) {
    keys[e.getKeyCode()] = false;

}

@Override
public void keyTyped(KeyEvent e) {

}

public boolean KeyPressed(int keycode) {
    return keys[keycode];

}

public boolean KeyReleased(int keycode) {
   return keys[keycode];
 }
}

正方形应移动到的位置. KeyListener继承自GameAssistant(JFrame是使用KeyListener创建的)

Class where the square should move. KeyListener is inherited from GameAssistant(JFrame is created with the KeyListener)

package TestCode;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;

import javaGD.GameAssistant.GameAssistant;

public class Game extends GameAssistant {
public int x, xSpeed, y, ySpeed;

public Game(String title, int width, int height, boolean makeResizable) {
    super(title, width, height, makeResizable);

}

@Override
public void setup() {
    x = 0;
    y = 0;
    xSpeed = 5;
    ySpeed = 5;
}

@Override
public void update() {
    if (this.Keyboard().KeyReleased(KeyEvent.VK_D)) {
        x += xSpeed;
    }

    if (this.Keyboard().KeyReleased(KeyEvent.VK_A)) {
        x -= xSpeed;
    }

    if (this.Keyboard().KeyReleased(KeyEvent.VK_W)) {
        y -= ySpeed;
    }

    if (this.Keyboard().KeyReleased(KeyEvent.VK_S)) {
        y += ySpeed;
    }
}

@Override
public void draw(Graphics g) {

    g.setColor(Color.BLACK);
    g.fillRect(x, y, 20, 20);

}

}

推荐答案

KeyReleased应该返回!keys[keycode]否则释放时将返回false,按下时将返回true

KeyReleased should be returning !keys[keycode] Otherwise it will return false when released and true when pressed

public boolean KeyReleased(int keycode) {
   return !keys[keycode];
}

我还建议使用键绑定API 优于KeyListener,因为它更可靠,可重复使用.

I would also recommend using the Key bindings API over KeyListener as it's more reliable and re-usable.

如果您仅计划有限数量的输入操作,则可以使用Setenum,这样您就可以将方式"与内容"解耦.

If you are only planning on a limited number of input operations, I would use Set and a enum, this way you can decouple the "how" from the "what".

您的update方法不在乎如何"管理输入,只在乎什么是状态"

You update method doesn't care "how" the inputs are managed, only the "what is the state"

从概念上讲,也许像...

Conceptually, maybe something like...

public enum GameInput {
    UP, DOWN, LEFT, RIGHT;
}

public class KeyManager implements KeyListener {

    private Set<GameInput> inputs = new HashSet<>();

    public KeyManager() {
    }

    @Override
    public void keyPressed(KeyEvent e) {
        // Check the key code, verify if it's one of the configured
        // actions keys
        // The key code could come from a configuration file which might
        // be customisable by the user...
        if (e.getKeyCode() == KeyEvent.VK_W) {
            inputs.add(GameInput.UP);
        } else if (e.getKeyCode() == KeyEvent.VK_S) {
            // etc...
        } // etc...
    }

    @Override
    public void keyReleased(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_W) {
            inputs.remove(GameInput.UP);
        } else if (e.getKeyCode() == KeyEvent.VK_S) {
            // etc...
        } // etc...
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    public boolean isKeyPressed(GameInput input) {
        return inputs.contains(input);

    }

    public boolean isKeyReleased(GameInput input) {
        return !isKeyPressed(input);
    }
}

您的update方法可能看起来像...

And your update method might look like...

@Override
public void update() {
    if (this.Keyboard().isKeyReleased(GameInput.RIGHT)) {
        x += xSpeed;
    }

    if (this.Keyboard().isKeyReleased(GameInput.LEFT)) {
        x -= xSpeed;
    }

    if (this.Keyboard().isKeyReleased(GameInput.UP)) {
        y -= ySpeed;
    }

    if (this.Keyboard().isKeyReleased(GameInput.DOWN)) {
        y += ySpeed;
    }
}

现在,您的update方法不关心输入的生成方式",而只关心设置(或不设置)输入时的操作.

Now your update method doesn't care "how" the inputs are generated, only what to do when they are (or aren't) set.

我个人将使用InputManager类并进一步将其解耦,因此可以通过其他方式(例如按钮,鼠标输入,游戏手柄等)来生成输入.

Personally, I'd use a InputManager class and further decouple it, so inputs could be generate via other means, like buttons, mouse inputs, game pads, etc...

从概念上讲,这应该可行.我说从概念上讲",是因为在测试时遇到了许多怪异"问题,这些问题我对Java(1.8)或MacOS都无能为力-或因为它们是两者的结合...更多详细信息,请参见下文...

Conceptually, this should work. I say "conceptually", because while I was testing I came across a number of "weird" issues that I can't contribute to either Java (1.8) or MacOS - or because they are a combination of both...more details below...

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private Box box = new Box();

        public TestPane() {
            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap am = getActionMap();

            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "Up.pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "Up.released");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), "Down.pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), "Down.released");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "Left.pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "Left.released");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "Right.pressed");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "Right.released");

            am.put("Up.pressed", new KeyAction(InputAction.UP, true));
            am.put("Up.released", new KeyAction(InputAction.UP, false));
            am.put("Down.pressed", new KeyAction(InputAction.DOWN, true));
            am.put("Down.released", new KeyAction(InputAction.DOWN, false));
            am.put("Left.pressed", new KeyAction(InputAction.LEFT, true));
            am.put("Left.released", new KeyAction(InputAction.LEFT, false));
            am.put("Right.pressed", new KeyAction(InputAction.RIGHT, true));
            am.put("Right.released", new KeyAction(InputAction.RIGHT, false));

            Timer timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    box.update(getBounds());
                    repaint();
                }
            });
            timer.start();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            box.draw(g);
        }

    }

    public class Box {

        public int x, xSpeed, y, ySpeed, width, height;

        public Box() {
            x = 0;
            y = 0;
            xSpeed = 1;
            ySpeed = 1;
            width = 10;
            height = 10;
        }

        public void update(Rectangle bounds) {
            if (!InputManager.INSTANCE.isSet(InputAction.LEFT)) {
                x -= xSpeed;
            }
            if (!InputManager.INSTANCE.isSet(InputAction.RIGHT)) {
                x += xSpeed;
            }
            if (InputManager.INSTANCE.isSet(InputAction.UP) && InputManager.INSTANCE.isSet(InputAction.DOWN)) {
                //
            } else if (!InputManager.INSTANCE.isSet(InputAction.UP)) {
                y -= ySpeed;
            } else if (!InputManager.INSTANCE.isSet(InputAction.DOWN)) {
                y += ySpeed;
            }

            if (x < bounds.x) {
                x = 0;
            } else if (x + width > (bounds.x + bounds.width)) {
                x = bounds.x + (bounds.width - width);
            }
            if (y < bounds.y) {
                y = 0;
            } else if (y + height > (bounds.y + bounds.height)) {
                y = bounds.y + (bounds.height - height);
            }
        }

        public void draw(Graphics g) {
            g.setColor(Color.BLACK);
            g.fillRect(x, y, width, height);
        }
    }

    public enum InputAction {
        UP, DOWN, LEFT, RIGHT;
    }

    public enum InputManager {
        INSTANCE;

        private Set<InputAction> actions = new HashSet<>();

        public void set(InputAction action) {
            actions.add(action);
        }

        public void remove(InputAction action) {
            actions.remove(action);
        }

        public boolean isSet(InputAction action) {
            return actions.contains(action);
        }
    }

    public class KeyAction extends AbstractAction {

        private InputAction action;
        private boolean apply;

        public KeyAction(InputAction action, boolean apply) {
            this.action = action;
            this.apply = apply;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("!");
            if (apply) {
                System.out.println("Apply " + action);
                InputManager.INSTANCE.set(action);
            } else {
                System.out.println("Remove " + action);
                InputManager.INSTANCE.remove(action);
            }
        }
    }
}

TL; DR

在测试上面的示例时,我遇到了一个奇怪的问题,以前从未见过(不是我最近一直在做这种事情).

TL;DR

While testing the above example, I came across a bizarre number of issues, I've not seen before (not that I've been doing this kind of thing recently).

使用KeyListener时,如果我按下(并按住)两个按钮,则可以看到按下"操作,但是没有重复的事件,这是我通常期望的(对于任何键).当我松开琴键时,我看到了释放"动作,但是当我按下(并按住它)时,没有生成新的按下"动作.

When using KeyListener, if I pressed (and held) two buttons, I could see the "pressed" action, but there was no repeating events, which is something I would normally expect (for any keys). When I released on key, I saw the "release" action, but when I pressed (and held it), no new "press" action was generated.

我尝试了键绑定API(如上所述),但仍然没有成功(相似的结果).

I tried the key bindings API (as demonstrated above) and still had no success (similar results).

然后我将AWTEventListener直接附加到事件队列并监视所有按键.

I then attached a AWTEventListener directly to the event queue and monitored ALL the key strokes.

我注意到,有时(甚至只是反复敲击一个键)可能不会生成按下".

I noted that, some times (even is just tapping a key repeatedly) that "pressed" might not be generated.

我还注意到按住一个或多个键,再次释放并再次按下某个键(通常不是这样)并不会产生新的按下事件(仅释放事件)

I also noted that holding one or more keys down, releasing and pressing a key again, more often then not, did not generate a new press event (only release events)

我正在使用macOS 10.13.6和Java 1.8.0_144-b01-这可能是两者中的一个,也可能是错误,但我没有其他方法可以对其进行测试

I'm using macOS 10.13.6 and Java 1.8.0_144-b01 - it could be bug in either or both, but I don't have the means to test it otherwise

因此,从Java 1.8更新到Java 1.10之后,上述问题似乎已得到解决-但是,这突出了另一个硬件问题,即一次只能按下一定数量的键,请参阅-a href ="https://gaming.stackexchange.com/questions/6669/how-do-i-remove-the-limit-on-pc-keyboard-button-presses">如何取消PC键盘按钮的限制按下吗?了解更多详情

So, after updating from Java 1.8 to Java 1.10, the above mentioned issue seems to be resoled - however, this highlights another, hardware issue, where only a certain number of keys can be actively pressed at a time - See How do I remove the limit on PC keyboard button presses? for more details

这篇关于如何在Java中创建可用的KeyReleased方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆