Относительно того, «почему» у вас возникают проблемы, я могу только догадываться, поскольку у нас есть только неконтекстный фрагмент кода, однако есть ряд вещей, которые можно улучшить, которые могут помочьрешить проблему.
- Пользователь
WHEN_IN_FOCUSED_WINDOW
при вызове getInputMap
Это относится к контексту, когда ключевые события будут фактически инициированы, в вышеупомянутом случае ключевые событиябудет сработать, когда у окна будет фокус, независимо от того, какой компонент в данный момент имеет фокус клавиатуры.
- Предпочитать переопределение
paintComponent
над paint
Рисование в Swing - этоНекоторые сложные, как общая рекомендация, лучше переопределить paintComponent
, если вы хотите выполнить пользовательскую рисование.Приятным побочным эффектом этого является то, что он закрасит фоновый цвет компонентов для вас, на одну вещь меньше, что вам нужно сделать;)
- Предпочитать переопределение
getPreferredSize
, когда вам нужно предоставить подсказки по размеру
Переопределение getWidth
и getHeight
не вызовет конца возможных проблем, и его лучше всего избегать.Вместо этого переопределите getPreferredSize
, таким образом, вы участвуете в API макета и получаете все его преимущества, такие как возможность вызывать pack
на JFrame
и заставить его позаботиться обо всех странностях, связанных с декорациями кадров.
- Отделите изменение состояния от механизмов, используемых для воздействия на это состояние
Отличное слово для «развязки вашего кода».В вашем коде состояние плеера меняется непосредственно с помощью ключевых действий.Это не только плохая идея, она может привести к неожиданным побочным эффектам и становится все труднее управлять, поскольку требования становятся более сложными.Это также затрудняет изменение способа ввода.Например, вы можете включить различные методы ввода, такие как джойстики, но вам придется написать для него один и тот же код.
Вместо этого у вас должен быть просто «менеджер состояний», который переносит текущее состояние ввода, когда ваш «основной цикл» готов, он будет применять это состояние к модели как отдельный шаг.
«Основной цикл» не заботится о том, как обновляется состояние, он только может получить информацию, необходимую для принятия решения о том, как его применять.
Ниже приведен пример грубоговсе, что я обсуждал выше, и в моем тестировании у меня не было проблем с привязками "срыв"
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
public class ZombieMain extends JPanel implements ActionListener {
enum VerticalDirection {
UP, DOWN, NONE
}
enum HorizontalDirection {
LEFT, RIGHT, NONE
}
public class VerticalStateController {
private VerticalDirection state = VerticalDirection.NONE;
public void setState(VerticalDirection state) {
this.state = state;
}
public VerticalDirection getState() {
return state;
}
}
public class HorizontalStateController {
private HorizontalDirection state = HorizontalDirection.NONE;
public void setState(HorizontalDirection state) {
this.state = state;
}
public HorizontalDirection getState() {
return state;
}
}
public class VerticalAction extends AbstractAction {
private VerticalStateController controller;
private VerticalDirection state;
public VerticalAction(VerticalStateController controller, VerticalDirection state) {
this.controller = controller;
this.state = state;
}
@Override
public void actionPerformed(ActionEvent e) {
controller.setState(state);
}
}
public class HorizontalAction extends AbstractAction {
private HorizontalStateController controller;
private HorizontalDirection state;
public HorizontalAction(HorizontalStateController controller, HorizontalDirection state) {
this.controller = controller;
this.state = state;
}
@Override
public void actionPerformed(ActionEvent e) {
controller.setState(state);
}
}
private static int WIDTH = 400;
private static int HEIGHT = 400;
private Rectangle player = new Rectangle(0, 0, 20, 20);
private VerticalStateController verticalStateController = new VerticalStateController();
private HorizontalStateController horizontalStateController = new HorizontalStateController();
public ZombieMain() {
setBackground(new Color(40, 40, 40));
Timer t = new Timer(10, this);
t.start();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
// Pressed
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "Pressed.up");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), "Pressed.down");
// Released
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "Released.up");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), "Released.down");
// Pressed
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), "Pressed.left");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "Pressed.right");
// Released
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "Released.left");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "Released.right");
am.put("Pressed.up", new VerticalAction(verticalStateController, VerticalDirection.UP));
am.put("Pressed.down", new VerticalAction(verticalStateController, VerticalDirection.DOWN));
am.put("Released.up", new VerticalAction(verticalStateController, VerticalDirection.NONE));
am.put("Released.down", new VerticalAction(verticalStateController, VerticalDirection.NONE));
am.put("Pressed.left", new HorizontalAction(horizontalStateController, HorizontalDirection.LEFT));
am.put("Pressed.right", new HorizontalAction(horizontalStateController, HorizontalDirection.RIGHT));
am.put("Released.left", new HorizontalAction(horizontalStateController, HorizontalDirection.NONE));
am.put("Released.right", new HorizontalAction(horizontalStateController, HorizontalDirection.NONE));
}
@Override
public Dimension getPreferredSize() {
return new Dimension(WIDTH, HEIGHT);
}
public void actionPerformed(ActionEvent e) {
switch (verticalStateController.getState()) {
case UP:
player.y -= 4;
break;
case DOWN:
player.y += 4;
break;
}
switch (horizontalStateController.getState()) {
case LEFT:
player.x -= 4;
break;
case RIGHT:
player.x += 4;
break;
}
repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.fill(player);
g2d.dispose();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new ZombieMain());
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
Я должен отметить, что это только один подход.Могли бы также поставить кучу "флагов" в Set
какой-то