2013-05-02 15 views
6

Tworzę bardzo prostą grę pongową w Javie i robię to używając KeyListener. Chcę, aby tak było, gdy użytkownik naciśnie prawy lub lewy klawisz na klawiaturze, blok ponga idzie w tym kierunku. Jest to dość proste zadanie, ale dowiaduję się, że gdy użytkownik przytrzyma klawisz, blok przesuwa się jeden raz, zatrzymuje się na krótki czas, a następnie kontynuuje ruch do momentu zwolnienia klawisza przez użytkownika. Zauważam, że dzieje się tak podczas próby przytrzymania klawisza literowego na komputerze. Jeśli staram się przytrzymać „” klucza, komputer zrobi:Java KeyListener zacina

się [Pause] aaaaaaaaaaaaaaaa

Czy istnieje jakiś sposób, aby wyłączyć tę zacinać, bo robi się na drodze płynna rozgrywka dla mojej małej gry. Szybka naprawa byłaby bardzo doceniana.

Odpowiedz

5
  1. Należy użyć Key Bindings jak one rozwiązać większość problemów związanych z ostrości i są na ogół bardziej elastyczne ...
  2. Trzeba zdefiniować flagę, aby wskazać, gdy klawisz jest wciśnięty. Podczas gdy wciśnięty, nie należy wykonywać żadnych dodatkowych czynności ...

Przykłady ...

Zaktualizowany przez simpl Przykład:

W większości gier powinieneś reagować na "zmiany stanu", a nie na rzeczywiste kluczowe zdarzenia. Oznacza to, że zdarzenie, które faktycznie zmienia stan może być zmienna (myślę niestandardowych klawiszy)

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

Pierwotnie miałem odpowiedź na temat powiązań kluczowych, ale po niewielkich testach odkryłem, że nadal mają ten sam problem z jąkaniem. +1 dla innych rzeczy. – syb0rg

+0

@ syb0rg Dlatego potrzebujesz tej małej flagi;). Ostatni przykład demonstruje zgrabny pomysł, aby umożliwić zdarzeniu kluczowemu zastosowanie delty przyspieszenia, im dłużej zostanie naciśnięty, tym bardziej zwiększa się delta, dopóki nie zostanie zwolniony, a delta zostanie odwrócona, spowalniając obiekt w czasie;) – MadProgrammer

+1

@ syb0rg Dodano prosty przykład demonstrujący zasadę;) – MadProgrammer

7

pierwotnie miał odpowiedź na temat klawiszy, ale po trochę badań stwierdziliśmy, że nadal miał ten sam zacinać problem.

Nie należy polegać na częstości powtarzania systemu operacyjnego. Może być różna dla każdej platformy, a użytkownik może ją również dostosować.

Zamiast tego użyj Timera, aby zaplanować wydarzenie. Uruchamiasz Timer na keyPressed i zatrzymujesz Timer na 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); 
    } 

} 

Kod ten został przetestowany na Windows, gdzie kolejność zdarzeń jest keyPressed, keyPressed, keyPressed ... keyReleased.

Jednak myślę, że na Macu (lub Unixie) kolejność zdarzeń to keyPressed, keyReleased, keyPressed, keyReleased ... Więc nie jestem pewien, czy ten kod będzie działał lepiej niż twój obecny kod.

+0

Po prostu FYI, ten cytat jest ode mnie, ale nie jestem OP. – syb0rg

1

Dobrym pomysłem jest ustawienie wartości logicznych dla kluczy, które chcesz śledzić, a następnie na zdarzenie keypressed aktywować jeden z booleans, a po zwolnieniu klawisza dezaktywować. Usunie to opóźnienie klawiszy i pozwoli na wielokrotne naciśnięcia klawiszy!