2013-05-02 5 views
6

저는 Java에서 매우 간단한 pong 게임을 만들고 있습니다. KeyListener를 사용하여이 작업을하고 있습니다. 사용자가 키패드의 오른쪽 또는 왼쪽 키를 누르면 퐁 블록이 그 방향으로 이동하기를 원합니다. 이것은 간단한 작업이지만 사용자가 키를 누르고 있으면 블록이 한 번 이동하고 잠시 멈추고 사용자가 키를 놓을 때까지 계속 움직인다는 것을 알 수 있습니다. 컴퓨터에서 문자 키를 누르고 있으면이 문제가 발생합니다. 나는 'A'키를 누른 상태하려고하면 컴퓨터가 수행합니다Java KeyListener stutters

A [일시 정지] aaaaaaaaaaaaaaaa

이 더듬을 사용하지 않도록 어떻게든지 거기에, 그것은의 방법으로 받고 있기 때문에 내 작은 게임을위한 부드러운 게임 플레이. 빠른 수정은 깊이 감사 할 것입니다.

답변

5
  1. 가 초점 관련 문제의 대부분을 해결하고 일반적으로 더 유연과 같이 Key Bindings를 사용해야합니다 ...
  2. 당신은 키를 누를 때 표시하는 플래그를 정의해야합니다. 눌려있는 동안, 당신은 키 동안 물체를 가속하기위한 메커니즘을 보여줍니다

... 추가 작업을 수행하지 않아야)

간단하게 업데이트 됨 예 :

대부분의 게임에서 실제 키 이벤트보다는 "상태 변경"에 반응해야합니다. 이것은 실제로 상태가 변수가 될 수 변경 이벤트가 내가 원래 키 바인딩에 대한 대답을했다

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

원래 나는 Key Bindings에 대한 답변을 가지고 있었지만 약간의 테스트를 거친 후에도 똑같은 문제가 계속 발생했다. +1하지만 다른 것들. – syb0rg

+0

@ syb0rg 그 이유는 그 작은 깃발이 필요합니다;). 마지막 예제는 키 이벤트가 가속 델타를 적용하도록 허용하는 것에 대한 깔끔한 아이디어를 보여줍니다. 누를 때마다 델타가 증가하고 델타가 반전되고 시간이 지남에 따라 오브젝트가 느려질 때까지 증가합니다.) – MadProgrammer

+1

@ syb0rg 원칙을 설명하기위한 간단한 예제를 추가했습니다.) – MadProgrammer

7

(사용자 정의 키를 생각)하지만, 약간의 테스트 후 나는 그들이 여전히 같은 말을 더듬을 남겼 것을 의미한다 문제.

OS의 반복 속도에 의존하지 마십시오. 모든 플랫폼마다 다를 수 있으며 사용자가 사용자 정의 할 수도 있습니다.

대신 타이머를 사용하여 일정을 예약하십시오. keyPressed에서 Timer를 시작하고 keyReleased에서 Timer를 중지합니다. 이벤트의 순서의 keyPressed,의 keyPressed,의 keyPressed ...의 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<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에서 테스트되었습니다.

그러나 Mac (또는 Unix)에서 이벤트 순서가 keyPressed, keyReleased, keyPressed, keyReleased ...라고 생각합니다. 따라서이 코드가 현재 코드보다 더 잘 작동하는지 확신 할 수 없습니다.

+0

참고로, 그 견적은 저로부터 왔지만, 저는 OP가 아닙니다. – syb0rg

1

추적 할 키의 부울 값을 설정 한 다음 키 입력 이벤트에서 부울 중 하나를 활성화하고 해제 된 키를 비활성화하는 것이 좋습니다. 그것은 열쇠의 지체를 제거하고 여러 키를 눌러도 허용합니다!