2013-06-04 2 views
1

Я пытаюсь сделать программу в java, которая включает в себя постоянное перемещение объекта из одного нажатия клавиши. Подумайте, Pacman, где вы нажимаете один раз, и Pacman продолжает подниматься, пока вы не нажмете еще одну клавишу. Я хочу, чтобы код был простым, если это возможно. Мое первоначальное движение (один нажатие = одно движение), как это:Непрерывное движение одним нажатием клавиши?

public class AL extends KeyAdapter { 
    public void keyPressed(KeyEvent e){ 
     int keyCode = e.getKeyCode(); 
     if(keyCode == e.VK_A){ 
      x -= 5; 
     } 
     if(keyCode == e.VK_D){ 
      x += 5; 
     } 
     if(keyCode == e.VK_W){ 
      y -= 5; 
     } 
     if(keyCode == e.VK_S){ 
      y += 5; 
     } 

    } 

х и у значений являются положение овала. Это работает отлично, но я хочу, чтобы он продолжал двигаться после того, как я нажимаю клавишу только один раз, вместо того, чтобы держать его, чтобы сохранить движение. Я пробовал цикл while с логическим параметром, который перемещается в то время как true, и не принимает значение false, но как только я активирую цикл, он замораживает программу. Вот пример того, что бит кода:

public void keyPressed(KeyEvent e){ 
     int keyCode = e.getKeyCode(); 
     if(keyCode == e.VK_LEFT && moveL==false){ 
      moveL=true; 
      moveR=false; 
      moveU=false; 
      moveD=false; 
      while(moveL){ 
      x--; 
      } 
     } 

Пожалуйста, помогите мне понять это, я пытался и озираясь в течение нескольких дней в настоящее время. Я ценю любую помощь, которую вы, ребята, можете дать. Благодарю.

+1

Как это ' в то время как цикл цикла должен выйти? Если вы используете Swing, этот метод теперь блокирует тему Dispatching Event, что означает, что никакие новые ключевые события не могут быть переданы вашему приложению ... – MadProgrammer

+0

Я использую Swing. Это объясняет, почему это не работает, но без цикла while я не вижу, как я мог сохранить движение. – AlexanderJ93

+0

Обычно у вас будет какое-то событие таймера, которое позволит вам регулярно обновлять состояние пользовательского интерфейса. Взгляните на [этот пример] (http://stackoverflow.com/questions/16290543/using-keypad-to-move-a-circle-at-angles-in-java/16290659#16290659) – MadProgrammer

ответ

1

Основная концепция вращается вокруг этой идеи значение «дельта» или «изменение». Это значение затем применяется к состоянию, которое вы хотите изменить, либо увеличивая, либо уменьшая его значение.

Из-за природы Swing вы не можете заблокировать Thread Dispatching Thread, в противном случае вы не сможете обработать входящие события (такие как краска и ключевые события).

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

Несмотря на то, что вы можете применять трюки для облегчения этих требований, самым простым является использование javax.swing.Timer, которое запускает событие actionPerformed на регулярной основе в пределах EDT.

Когда это происходит, вы «обновляете» все элементы на заданную величину и перерисовываете экран.

enter image description hereenter image description here

import java.awt.BorderLayout; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.KeyEvent; 
import java.awt.geom.AffineTransform; 
import java.awt.image.BufferedImage; 
import java.io.File; 
import java.io.IOException; 
import javax.imageio.ImageIO; 
import javax.swing.AbstractAction; 
import javax.swing.ActionMap; 
import javax.swing.InputMap; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.KeyStroke; 
import javax.swing.Timer; 
import javax.swing.UIManager; 
import javax.swing.UnsupportedLookAndFeelException; 

public class PacManTest { 

    public static void main(String[] args) { 
     new PacManTest(); 
    } 

    public PacManTest() { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
       } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { 
       } 

       JFrame frame = new JFrame("Test"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.setLayout(new BorderLayout()); 
       frame.add(new MazePane()); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 

    public class PacMan { 

     private int x; 
     private int y; 

     private int deltaX; 
     private int deltaY; 

     private BufferedImage sprite; 

     public PacMan() { 
      try { 
       sprite = ImageIO.read(new File("PacMan.png")); 
      } catch (IOException ex) { 
       ex.printStackTrace(); 
      } 
     } 

     public void move(int x, int y) { 
      deltaX = x; 
      deltaY = y; 
     } 

     public void update(MazePane pane) { 
      x += deltaX; 
      y += deltaY; 
      if (x + sprite.getWidth() > pane.getWidth()) { 
       x = pane.getWidth() - sprite.getWidth(); 
      } else if (x < 0) { 
       x = 0; 
      } 
      if (y + sprite.getHeight() > pane.getHeight()) { 
       y = pane.getHeight() - sprite.getHeight(); 
      } else if (y < 0) { 
       y = 0; 
      } 
     } 

     public void paint(MazePane pane, Graphics2D g2d) { 
      Graphics2D g = (Graphics2D) g2d.create(); 

      float angle = 0; 
      if (deltaX != 0) { 
       angle = deltaX > 0 ? 0 : 180; 
      } else if (deltaY != 0) { 
       angle = deltaY > 0 ? 90 : 270;     
      } 
      AffineTransform t = new AffineTransform(); 
      t.translate(x, y); 
      t.rotate(Math.toRadians(angle), sprite.getWidth()/2, sprite.getHeight()/2); 
      g.setTransform(t); 
      g.drawImage(sprite, 0, 0, pane); 
      g.dispose(); 
     } 

    } 

    public class MazePane extends JPanel { 

     private PacMan pacMan; 
     public MazePane() { 
      pacMan = new PacMan(); 
      Timer timer = new Timer(40, new ActionListener() { 
       @Override 
       public void actionPerformed(ActionEvent e) { 
        pacMan.update(MazePane.this); 
        repaint(); 
       } 
      }); 
      timer.start(); 
      InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW); 
      ActionMap am = getActionMap(); 

      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "left"); 
      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "right"); 
      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "up"); 
      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "down"); 

      am.put("left", new MoveAction(pacMan, -4, 0)); 
      am.put("right", new MoveAction(pacMan, 4, 0)); 
      am.put("up", new MoveAction(pacMan, 0, -4)); 
      am.put("down", new MoveAction(pacMan, 0, 4)); 
     } 

     @Override 
     public Dimension getPreferredSize() { 
      return new Dimension(200, 200); 
     } 

     @Override 
     protected void paintComponent(Graphics g) { 
      super.paintComponent(g); 
      Graphics2D g2d = (Graphics2D) g.create(); 
      pacMan.paint(this, g2d); 
      g2d.dispose(); 
     } 

     public class MoveAction extends AbstractAction { 

      private int deltaX; 
      private int deltaY; 
      private PacMan pacMan; 

      public MoveAction(PacMan pacMan, int deltaX, int deltaY) { 
       this.deltaX = deltaX; 
       this.deltaY = deltaY; 
       this.pacMan = pacMan; 
      } 

      @Override 
      public void actionPerformed(ActionEvent e) { 
       pacMan.move(deltaX, deltaY); 
      } 

     }   
    } 
} 

Я также рекомендовал бы, что вы потратите время, чтобы узнать о Key Bindings, KeyListener страдают от проблем фокуса, какие ключевые переплеты способны адресовать ...

0

Вам необходимо обработать перемещение в отдельном потоке. То есть:

public class Pacman implements Runnable 
{ 
    public void run(){ 
     //moving code, i.e. in a while loop 

     //every move will notify the EDT: 
     SwingUtilities.invokeLater(new Runnable(){ 
      public void run(){ 
       //update the Swing here - i.e. move Pacman 
      } 
     } 
    } 

    public void startMoving(){ 
     new Thread(this).start(); 
    } 

    //more methods to set speed, direction, etc... 
} 

Затем сохранить ссылку на экземпляр класса Pacman в классе Гуй и реагировать на различные нажатия клавиш путем изменения параметров pacman в:

public void keyPressed(KeyEvent e){ 
    int keyCode = e.getKeyCode(); 
    if(keyCode == e.VK_LEFT){ 
     pacman.newDirection(LEFT); //for exmaple, enum with direction LEFT, RIGHT, UP, DOWN... 
    } 
    //etc... more logic 
} 
+0

Прямо сейчас, у меня есть только один класс, мой класс GUI ... Нужно ли иметь два отдельных класса? И какова цель класса pacman, который вы используете? – AlexanderJ93

+0

@ AlexanderJ93 Нет, это не обязательно, только обычная практика заключается в разделении логического и графического интерфейса пользователя. Класс Pacman отделяет логику (pacman) от графического интерфейса пользователя (GUI-класс). –

Смежные вопросы