【问题标题】:How to create a usable KeyReleased method in java如何在 java 中创建可用的 KeyReleased 方法
【发布时间】:2018-08-15 21:32:58
【问题描述】:

我是挥杆新手,我试图让方格移动,但只有在释放键时(例如 W),但当我按住键时方格才会移动

KeyListener 类

我想确保一个键被按下,如果它仍然被按下它应该返回 false,但如果它被按下然后释放则返回 true 包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 创建的)

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);

}

}

【问题讨论】:

  • 据我了解,如果您按住某个键一段时间然后松开该键,那么在 Windows 上您将收到诸如“按下、按下、按下...释放”之类的事件。在 Mac 上,您会得到“按下、释放、按下、释放、按下、释放……”。这就是为什么侦听移动玩家的事件通常会侦听“按下”事件。

标签: java swing keylistener


【解决方案1】:

KeyReleased 应该返回 !keys[keycode] 否则它会在释放时返回 false 并在按下时返回 true

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

我还建议使用Key bindings API 而不是KeyListener,因为它更可靠且可重复使用。

如果您只计划有限数量的输入操作,我会使用Setenum,这样您就可以将“如何”与“什么”分离。

update 方法不关心“如何”管理输入,只关心“什么是状态”

从概念上讲,可能类似于...

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 方法可能看起来像...

@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 方法并不关心输入是“如何”生成的,只关心设置(或未设置)时要做什么。

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

可运行示例...

从概念上讲,这应该可行。我说“概念上”,因为当我在测试时遇到了一些“奇怪”的问题,这些问题我无法为 Java(1.8)或 MacOS 做出贡献——或者因为它们是两者的结合……更多详情如下...

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

在测试上面的例子时,我遇到了很多奇怪的问题,我以前没有见过(不是我最近一直在做这种事情)。

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

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

然后我将AWTEventListener 直接附加到事件队列并监控所有击键。

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

我还注意到,按住一个或多个键,释放并再次按下一个键,通常情况下,不会生成新的按下事件(仅释放事件)

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

更新...

因此,在从 Java 1.8 更新到 Java 1.10 后,上述问题似乎得到了解决 - 然而,这突出了另一个硬件问题,一次只能主动按下一定数量的键 - 请参阅 @987654322 @了解更多详情

【讨论】:

  • 我试过了,它就像 KeyPressed 方法
  • 虽然我对此摸不着头脑,但“默认”状态是什么 - 你不能同时左右移动...
  • 好的,我花了一点时间来整理一些东西,但是,KeyListener 不喜欢按下一个键,然后尝试从不同的键响应新的释放/按下事件://
  • 我已经能够实现您的目标,因为 API 似乎不支持它(至少在 Mac 上)。当我按住两个键并释放一个键时,我无法为其他键生成新的按键事件,直到我释放另一个键
  • 好的,经过更多调试 - 我似乎在 macOS 或 Java 或两者中发现了一个错误。当按住一个键时,KeyListener(以及键绑定 API 和AWTEventListener)应该生成重复的键事件——但在我的测试中它们不是。它也不会在应该生成按键事件时生成按键事件,但始终会生成按键释放按键事件。你的里程可能会更好
猜你喜欢
  • 2021-09-05
  • 1970-01-01
  • 2014-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-18
  • 1970-01-01
相关资源
最近更新 更多