Java KeyListener口吃

我正在java中做一个非常简单的pong游戏,我正在使用KeyListener来做这个。 我需要它,所以当用户按下键盘上的右键或者左键时,乒乓球就会沿着这个方向前进。 这是一个足够简单的任务,但是我发现,当用户按下键时,块会移动一次,短时间停止,然后继续移动,直到用户释放键。 我注意到,当您试图按住计算机上的一个字母键时会发生这种情况。 如果我试图按住“a”键,电脑将会执行:

a [pause] aaaaaaaaaaaaaaaa

有没有办法阻止这个口吃,因为它为我的小游戏带来了stream畅的游戏方式。 快速解决将深受赞赏。

我原本就有关键绑定的答案,但经过一些testing,我发现他们仍然有相同的口吃问题。

不要依赖于操作系统的重复率。 对于每个平台它可以是不同的,用户也可以定制它。

而是使用计时器来安排事件。 您在keyPressed上启动Timer,并在keyReleased上停止Timer。

import java.awt.*; import java.awt.event.*; import java.net.*; import java.util.Map; import java.util.HashMap; import javax.imageio.ImageIO; import javax.swing.*; public class KeyboardAnimation implements ActionListener { private final static String PRESSED = "pressed "; private final static String RELEASED = "released "; private final static Point RELEASED_POINT = new Point(0, 0); private JComponent component; private Timer timer; private Map<String, Point> pressedKeys = new HashMap<String, Point>(); public KeyboardAnimation(JComponent component, int delay) { this.component = component; timer = new Timer(delay, this); timer.setInitialDelay( 0 ); } public void addAction(String keyStroke, int deltaX, int deltaY) { // InputMap inputMap = component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); InputMap inputMap = component.getInputMap(); ActionMap actionMap = component.getActionMap(); String pressedKey = PRESSED + keyStroke; KeyStroke pressedKeyStroke = KeyStroke.getKeyStroke( pressedKey ); Action pressedAction = new AnimationAction(keyStroke, new Point(deltaX, deltaY)); inputMap.put(pressedKeyStroke, pressedKey); actionMap.put(pressedKey, pressedAction); String releasedKey = RELEASED + keyStroke; KeyStroke releasedKeyStroke = KeyStroke.getKeyStroke( releasedKey ); Action releasedAction = new AnimationAction(keyStroke, RELEASED_POINT); inputMap.put(releasedKeyStroke, releasedKey); actionMap.put(releasedKey, releasedAction); } private void handleKeyEvent(String keyStroke, Point moveDelta) { // Keep track of which keys are pressed if (RELEASED_POINT == moveDelta) pressedKeys.remove( keyStroke ); else pressedKeys.put(keyStroke, moveDelta); // Start the Timer when the first key is pressed if (pressedKeys.size() == 1) { timer.start(); } // Stop the Timer when all keys have been released if (pressedKeys.size() == 0) { timer.stop(); } } // Invoked when the Timer fires public void actionPerformed(ActionEvent e) { moveComponent(); } // Move the component to its new location private void moveComponent() { int componentWidth = component.getSize().width; int componentHeight = component.getSize().height; Dimension parentSize = component.getParent().getSize(); int parentWidth = parentSize.width; int parentHeight = parentSize.height; // Calculate new move int deltaX = 0; int deltaY = 0; for (Point delta : pressedKeys.values()) { deltaX += delta.x; deltaY += delta.y; } // Determine next X position int nextX = Math.max(component.getLocation().x + deltaX, 0); if ( nextX + componentWidth > parentWidth) { nextX = parentWidth - componentWidth; } // Determine next Y position int nextY = Math.max(component.getLocation().y + deltaY, 0); if ( nextY + componentHeight > parentHeight) { nextY = parentHeight - componentHeight; } // Move the component component.setLocation(nextX, nextY); } private class AnimationAction extends AbstractAction implements ActionListener { private Point moveDelta; public AnimationAction(String keyStroke, Point moveDelta) { super(PRESSED + keyStroke); putValue(ACTION_COMMAND_KEY, keyStroke); this.moveDelta = moveDelta; } public void actionPerformed(ActionEvent e) { handleKeyEvent((String)getValue(ACTION_COMMAND_KEY), moveDelta); } } public static void main(String[] args) { JPanel contentPane = new JPanel(); contentPane.setLayout( null ); Icon dukeIcon = null; try { dukeIcon = new ImageIcon( "dukewavered.gif" ); // dukeIcon = new ImageIcon( ImageIO.read( new URL("http://duke.kenai.com/iconSized/duke4.gif") ) ); } catch(Exception e) { System.out.println(e); } JLabel duke = new JLabel( dukeIcon ); duke.setSize( duke.getPreferredSize() ); duke.setLocation(100, 100); contentPane.add( duke ); KeyboardAnimation navigation = new KeyboardAnimation(duke, 24); navigation.addAction("LEFT", -3, 0); navigation.addAction("RIGHT", 3, 0); navigation.addAction("UP", 0, -3); navigation.addAction("DOWN", 0, 3); navigation.addAction("A", -5, 0); navigation.addAction("S", 5, 0); navigation.addAction("Z", 0, -5); navigation.addAction("X", 0, 5); navigation.addAction("V", 5, 5); JFrame frame = new JFrame(); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); // frame.getContentPane().add(new JTextField(), BorderLayout.SOUTH); frame.getContentPane().add(contentPane); frame.setSize(600, 600); frame.setLocationRelativeTo( null ); frame.setVisible(true); } } 

此代码已在Windows上进行了testing,其中事件的顺序是keyPressed,keyPressed,keyPressed … keyReleased。

不过,我认为在Mac(或Unix)上,事件的顺序是keyPressed,keyReleased,keyPressed,keyReleased …所以我不确定这个代码是否会比你当前的代码更好。

  1. 你应该使用键绑定,因为他们解决了大多数焦点相关的问题,一般都更灵活…
  2. 您需要定义一个标志来指示何时按下某个键。 按下时,您不应该执行任何其他任务…

例如…

  • Java对象移动
  • 使用小键盘在java中以angular度移动一个圆
  • Java的Paint方法的问题,刷新速度很滑稽 ,它显示了按下键时加速对象的机制;)

用简单的例子更新

在大多数游戏中,你应该对“状态变化”做出反应,而不是对实际的关键事件做出反应 这意味着实际改变状态的事件可以是可变的(认为自定义键)

 import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import javax.swing.AbstractAction; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.KeyStroke; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class SinglePressKeyBinding { public static void main(String[] args) { new SinglePressKeyBinding(); } public SinglePressKeyBinding() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TestPane extends JPanel { private JLabel message; private boolean spacedOut = false; public TestPane() { message = new JLabel("Waiting"); setLayout(new GridBagLayout()); add(message); InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW); ActionMap am = getActionMap(); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false), "space-pressed"); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true), "space-released"); am.put("space-pressed", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { if (spacedOut) { message.setText("I'm ignoring you"); } else { spacedOut = true; message.setText("Spaced out"); } } }); am.put("space-released", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { spacedOut = false; message.setText("Back to earth"); } }); } @Override public Dimension getPreferredSize() { return new Dimension(200, 200); } } } 

一个好主意是为你想要跟踪的键设置布尔值,然后在按键事件中激活其中一个布尔值,并在释放的键上禁用它。 它将消除键的滞后,并允许多个按键!

Interesting Posts