Java KeyListener заикается

Я делаю очень простую игру в понг в java, и я делаю это с помощью KeyListener. Я хочу, чтобы так, когда пользователь нажимает на клавиатуру правую или левую клавиши, блок понга идет в этом направлении. Это достаточно простая задача, но я обнаружил, что когда пользователь удерживает ключ, блок перемещается один раз, останавливается на короткое время, а затем продолжает движение, пока пользователь не освободит ключ. Я замечаю, что это происходит, когда вы пытаетесь удержать буквенный ключ на компьютере. Если я попытаюсь удерживать клавишу «a», компьютер будет делать:

a [пауза] aaaaaaaaaaaaaaaa

Есть ли что-то, чтобы отключить это заикание, потому что это мешает гладкому геймплею для моей маленькой игры. Быстрое исправление будет глубоко оценено.

Первоначально у меня был ответ о Key Bindings, но после небольшого тестирования я обнаружил, что у них все еще была одна и та же проблема заикания.

Не полагайтесь на частоту повторения ОС. Он может быть различным для каждой платформы, и пользователь может также настроить его.

Вместо этого используйте таймер для планирования события. Вы запускаете таймер на клавише «Подпрессованный» и останавливаете таймер на keyReleased.

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 pressedKeys = new HashMap(); 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://sofru.miximages.com/keypad/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, где порядок событий keyPressed, keyPressed, keyPressed … keyReleased.

Тем не менее, я думаю, что на Mac (или Unix) порядок событий будет keyPressed, keyReleased, keyPressed, keyReleased … Поэтому я не уверен, будет ли этот код работать лучше вашего текущего кода.

  1. Вы должны использовать Key Bindings, поскольку они решают большинство проблем, связанных с фокусом, и, как правило, более гибкие …
  2. Вам нужно определить флаг, указывающий, когда нажата клавиша. При нажатии вы не должны выполнять никаких дополнительных задач …

Например…

  • Движение объектов Java
  • Использование клавиатуры для перемещения круга под углами в java
  • Проблемы с методом Paint в Java, смехотворная скорость обновления, которая показывает механизм ускорения объекта при нажатии клавиши;)

Обновлено простым примером

В большинстве игр вы должны реагировать на «изменения состояния», а не на фактические ключевые события. Это означает, что событие, которое фактически изменяет состояние, может быть переменным (думаю, пользовательские ключи)

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

Хорошая идея – установить логические значения для тех клавиш, которые вы хотите отслеживать, а затем в событии с нажатой клавишей активировать одно из булевых элементов, а на клавише – деактивировать. Он удалит задержку с ключами и даст возможность нажатия нескольких клавиш!

  • Обнаружение текущего языка ввода iPhone
  • iOS 8.1 Симулятор всегда использует раскладку клавиатуры США, несмотря на немецкую аппаратную клавиатуру
  • Получение размеров мягкой клавиатуры
  • Клавиатура iMessage Receding в приложении iOS
  • Слушайте нажатие клавиши, когда программа находится в фоновом режиме
  • iPhone - Клавиатура скрывает TextField
  • Пользовательская клавиатура iOS 8: изменение высоты
  • Создание SoftKeyboard с несколькими / альтернативными символами на ключ
  • UISearchbar clearButton заставляет клавиатуру появляться
  • Используйте клавишу «ENTER» на программной клавиатуре вместо кнопки
  • Моделирование событий нажатия клавиш в Mac OS X
  • Давайте будем гением компьютера.