Основная концепция вращается вокруг этой идеи значение «дельта» или «изменение». Это значение затем применяется к состоянию, которое вы хотите изменить, либо увеличивая, либо уменьшая его значение.
Из-за природы Swing вы не можете заблокировать Thread Dispatching Thread, в противном случае вы не сможете обработать входящие события (такие как краска и ключевые события).
В равной степени вы никогда не должны пытаться обновлять любой компонент пользовательского интерфейса (или переменную состояния, которая может влиять на пользовательский интерфейс) из любого потока, кроме EDT.
Несмотря на то, что вы можете применять трюки для облегчения этих требований, самым простым является использование javax.swing.Timer
, которое запускает событие actionPerformed
на регулярной основе в пределах EDT.
Когда это происходит, вы «обновляете» все элементы на заданную величину и перерисовываете экран.
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
страдают от проблем фокуса, какие ключевые переплеты способны адресовать ...
Как это ' в то время как цикл цикла должен выйти? Если вы используете Swing, этот метод теперь блокирует тему Dispatching Event, что означает, что никакие новые ключевые события не могут быть переданы вашему приложению ... – MadProgrammer
Я использую Swing. Это объясняет, почему это не работает, но без цикла while я не вижу, как я мог сохранить движение. – AlexanderJ93
Обычно у вас будет какое-то событие таймера, которое позволит вам регулярно обновлять состояние пользовательского интерфейса. Взгляните на [этот пример] (http://stackoverflow.com/questions/16290543/using-keypad-to-move-a-circle-at-angles-in-java/16290659#16290659) – MadProgrammer