2013-05-02 99 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

一個好主意是爲你想要跟蹤的鍵設置布爾值,然後在按鍵事件中激活其中一個布爾值,然後在釋放的鍵上停用它。它將消除按鍵的滯後,並允許多個按鍵!