2013-05-02 90 views
6

我正在java中制作一个非常简单的pong游戏,我正在使用KeyListener进行此操作。我想要它,所以当用户按下键盘上的右键或者左键时,乒乓块就会朝这个方向行进。这是一个足够简单的任务,但我发现当用户按住键时,块会移动一次,短时间停止,然后继续移动,直到用户释放键。我注意到这种情况发生在您试图按住计算机上的字母键时。如果我尝试按住“A”键,计算机会做什么:Java KeyListener口吃

一个[暂停] aaaaaaaaaaaaaaaa

有什么方法来禁用这个口吃,因为它是在的方式获得我的小游戏流畅的游戏。快速解决将深受赞赏。

回答

5
  1. 你应该使用Key Bindings,因为他们解决了广大的焦点相关的问题,并且通常更灵活...
  2. 您需要定义一个标志,当按下一个键表示。当按下时,你不应该执行任何其他任务...

为例子...

更新为simp例如

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

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

    } 
} 
+0

我最初有一个关于键绑定的答案,但经过一些测试后,我发现他们仍然有相同的口吃问题。然而,对于其他的东西+1。 – syb0rg 2013-05-02 01:47:42

+0

@ syb0rg这就是为什么你需要那个小旗子;)。最后一个例子展示了允许按键事件应用加速度增量的简洁想法,按下时间越长,增量增加的越多,直到它被释放并且增量反转,随着时间推移减慢对象;) – MadProgrammer 2013-05-02 01:51:55

+1

@ syb0rg添加了一个简单的例子来演示原理;) – MadProgrammer 2013-05-02 01:58:57

7

我本来约按键绑定一个答案,但一个小的测试后,我发现,他们仍然有同样的口吃问题。

不要依赖OS的重复率。它对于每个平台都可能不同,用户也可以对其进行定制。

而是使用计时器来安排事件。您在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上进行了测试,其中事件的顺序是keyPressed,keyPressed,keyPressed ... keyReleased。然而,我认为在Mac(或Unix)上,事件的顺序是keyPressed,keyReleased,keyPressed,keyReleased ...所以我不确定这段代码是否会比当前代码更好地工作。

+0

只是FYI,这个引用来自我,但我不是OP。 – syb0rg 2013-05-03 20:53:05

1

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