所以我看了很多关于这个问题的不同主题,但是有关如何使其发挥作用的所有建议;我做了,所以它应该工作。我之前已经开始工作了,我正在做同样的事情,就像我的图像在我的另一个程序中移动一样。无论如何,这是一款类似游戏的太空射击游戏。我正在为负责玩家与玩家的班级工作。您会看到空格键的注释和键' e'将在稍后添加一个射击。射击是另一个类,这样我可以快速射击并且可以控制每个子弹因为有独立的物体。无论如何,我做了一些测试,打印出来的东西,我知道计时器工作。我知道移动方法正常,因为我看到我的图像在屏幕上移动。但是,我无法控制。然后我在按键区域放了一个打印语句,它没有打印任何东西。所以我知道这是错误的代码。所以任何帮助都会很棒,因为我很难过。这不适合大学或高中的课程。这是一个个人项目。这是代码:
import javax.swing.*;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class PlayervsPlayer implements KeyListener,ActionListener{
private JFrame window;
private Timer timer;
private int win_size;
private int ship_clearence=45;
private int speed=3;
private final int stop1=0;
private final int stop2=1;
private final int left1=2;
private final int left2=3;
private final int right1=4;
private final int right2=5;
private int dir1=left1;
private int dir2=right2;
Sprite background;
Sprite ship1=new Sprite("Spaceship.png");
Sprite ship2=new Sprite("Spaceship2.png");
public PlayervsPlayer(JFrame w,Sprite backdrop,int win_s) {
window=w;
background=backdrop;
win_size=win_s;
window.addKeyListener(this);
}
public void pvpmain() {
System.out.println("Player vs Player working!");
ship1.setSize(125,125);
ship1.setLocation((int)((win_size/2)-(ship1.getWidth()/2)),win_size-ship1.getHeight()-ship_clearence);
ship2.setSize(125,125);
ship2.setLocation((int)((win_size/2)-(ship1.getWidth()/2)),0);
window.add(ship1,0);
window.add(ship2,0);
window.repaint();
timer = new Timer( 10, this );
timer.start();
}
public void keyPressed(KeyEvent e) {
int key=e.getKeyCode();
if ( key == KeyEvent.VK_RIGHT ){
System.out.println("Right Key");//
dir1 = right1;
}
else if (key==KeyEvent.VK_LEFT) {
dir1=left1;
}
else if (key==KeyEvent.VK_SPACE) {
//Nothing for now as for shooting
}
if (key==KeyEvent.VK_Q) {
dir2=left2;
}
else if (key==KeyEvent.VK_W) {
dir2=right2;
}
else if (key==KeyEvent.VK_E) {
//Nothing for now as for shooting
}
}
public void move() {
if (dir1==left1) {
if (ship1.getX()<=speed) {
dir1=stop1;
}
else {
ship1.setLocation(ship1.getX()-speed,ship1.getY());
}
}
else if (dir1==right1) {
if (ship1.getX()+ship1.getWidth()>=win_size-speed) {
dir1=stop1;
}
else {
ship1.setLocation(ship1.getX()+speed,ship1.getY());
}
}
if (dir2==left2) {
if (ship2.getX()<=speed) {
dir2=stop2;
}
else {
ship2.setLocation(ship2.getX()-speed,ship2.getY());
}
}
else if (dir2==right2) {
if (ship2.getX()+ship2.getWidth()>=win_size-speed) {
dir2=stop2;
}
else {
ship2.setLocation(ship2.getX()+speed,ship2.getY());
}
}
}
public void actionPerformed(ActionEvent e) {
if ( e.getSource() == timer ){
System.out.println("Timer Working!");
move();
}
}
public void keyTyped(KeyEvent e) {
//Nothing
}
public void keyReleased(KeyEvent e) {
//Nothing
}
}
提前致谢。
答案 0 :(得分:1)
您的主要问题与KeyListener
在您尝试使用它的应用程序中不可靠的事实有关。 KeyListener
要求其注册的组件能够接收键盘焦点并且在触发键事件之前具有键盘焦点。很容易让焦点被其他组件窃取。
对您的问题最可靠的解决方案是使用key bindings API,这部分是为了解决这个问题而开发的。
您可能还希望阅读How to Use Actions以了解API的这一部分如何运作
因此,调整Trying to move JLabels on a JPanel with GridLayout中的代码,这是与您似乎尝试的内容最为一致的最简单的示例,您最终会得到类似的内容......
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel player;
public TestPane() {
player = makeLabel("P");
player.setSize(player.getPreferredSize());
add(player);
addKeyBinding("left", KeyEvent.VK_LEFT, new MoveAction(player, -4, 0));
addKeyBinding("right", KeyEvent.VK_RIGHT, new MoveAction(player, 4, 0));
addKeyBinding("up", KeyEvent.VK_UP, new MoveAction(player, 0, -4));
addKeyBinding("down", KeyEvent.VK_DOWN, new MoveAction(player, 0, 4));
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void addKeyBinding(String name, int keyCode, Action action) {
InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(KeyStroke.getKeyStroke(keyCode, 0), name);
actionMap.put(name, action);
}
protected JLabel makeLabel(String text) {
JLabel label = new JLabel(text);
label.setBorder(new CompoundBorder(
new LineBorder(Color.GRAY),
new EmptyBorder(4, 4, 4, 4)));
return label;
}
public class MoveAction extends AbstractAction {
private final int xDelta, yDelta;
private final JComponent component;
public MoveAction(JComponent component, int xDelta, int yDelta) {
this.component = component;
this.xDelta = xDelta;
this.yDelta = yDelta;
}
@Override
public void actionPerformed(ActionEvent e) {
Point location = component.getLocation();
location.x += xDelta;
location.y += yDelta;
component.setLocation(location);
repaint();
}
}
}
}
但是,多重同步的关键时刻呢?
嗯,这不是一个独特的问题,最常见的问题是使用一系列标志来确定当前是否按下了某个键。然后使用这些标志来确定如何最好地移动对象
因此,从第一个例子开始,你最终会得到类似......
的东西import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.Set;
import java.util.TreeSet;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
public class Test2 {
public static void main(String[] args) {
new Test2();
}
public Test2() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public enum Direction {
UP, DOWN, LEFT, RIGHT;
}
public class Controller {
private Set<Direction> directions;
private JComponent player;
public Controller(JComponent player) {
this.player = player;
directions = new TreeSet<>();
}
public void released(Direction direction) {
directions.remove(direction);
updatePosition();
}
public void pressed(Direction direction) {
directions.add(direction);
updatePosition();
}
protected void updatePosition() {
Point location = player.getLocation();
if (directions.contains(Direction.UP)) {
location.y -= 4;
}
if (directions.contains(Direction.DOWN)) {
location.y += 4;
}
if (directions.contains(Direction.LEFT)) {
location.x -= 4;
}
if (directions.contains(Direction.RIGHT)) {
location.x += 4;
}
player.setLocation(location);
}
}
public class TestPane extends JPanel {
private JLabel player;
public TestPane() {
player = makeLabel("P");
player.setSize(player.getPreferredSize());
add(player);
Controller controller = new Controller(player);
addKeyBinding("left", KeyEvent.VK_LEFT, Direction.LEFT, controller);
addKeyBinding("right", KeyEvent.VK_RIGHT, Direction.RIGHT, controller);
addKeyBinding("up", KeyEvent.VK_UP, Direction.UP, controller);
addKeyBinding("down", KeyEvent.VK_DOWN, Direction.DOWN, controller);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void addKeyBinding(String name, int keyCode, Direction direction, Controller controller) {
InputMap inputMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = getActionMap();
inputMap.put(KeyStroke.getKeyStroke(keyCode, 0, true), name + "-released");
inputMap.put(KeyStroke.getKeyStroke(keyCode, 0, false), name + "-pressed");
actionMap.put(name + "-released", new MoveAction(direction, controller, true));
actionMap.put(name + "-pressed", new MoveAction(direction, controller, false));
}
protected JLabel makeLabel(String text) {
JLabel label = new JLabel(text);
label.setBorder(new CompoundBorder(
new LineBorder(Color.GRAY),
new EmptyBorder(4, 4, 4, 4)));
return label;
}
public class MoveAction extends AbstractAction {
private Direction direction;
private Controller controller;
private boolean released;
public MoveAction(Direction direction, Controller controller, boolean released) {
this.direction = direction;
this.controller = controller;
this.released = released;
}
@Override
public void actionPerformed(ActionEvent e) {
if (released) {
controller.released(direction);
} else {
controller.pressed(direction);
}
}
}
}
}
下次尝试使用某些代码而不是强制它们重构并提出使用此键绑定的建议。如果您认为密钥绑定如此之大,为什么不用密钥绑定修改我的代码,让我们看看它有多复杂
叹息 - 因为我不应该,即使用你自己的话说,你不想要一个“复制和粘贴”的答案,但是,显然这就是你想要的......
import javax.swing.JFrame;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.beans.PropertyChangeListener;
// I'd prefer to extend from JPanel, as it provides bases for
// self contained responsibility, but apparently, that's too "advanced"
public class PlayervsPlayer implements ActionListener {
private JFrame window;
private Timer timer;
private int win_size;
private int ship_clearence = 45;
private int speed = 3;
private final int stop1 = 0;
private final int stop2 = 1;
private final int left1 = 2;
private final int left2 = 3;
private final int right1 = 4;
private final int right2 = 5;
private int dir1 = left1;
private int dir2 = right2;
// Sprite background;
JLabel ship1 = new JLabel("Spaceship.png");
JLabel ship2 = new JLabel("Spaceship2.png");
public PlayervsPlayer(JFrame w, int win_s) {
window = w;
// background = backdrop;
win_size = win_s;
JComponent contenPane = (JComponent) w.getContentPane();
InputMap inputMap = contenPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = contenPane.getActionMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "player1-left");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "player1-right");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), "player2-left");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_E, 0), "player2-right");
// I'd prefer a single "Move Action" class which could
// make these updates, but that might be "too advanced"
actionMap.put("player1-left", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 1 - left");
dir1 = left1;
}
});
actionMap.put("player1-right", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 1 - right");
dir1 = right1;
}
});
actionMap.put("player2-left", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 2 - left");
dir2 = left2;
}
});
actionMap.put("player2-right", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 2 - right");
dir2 = right2;
}
});
}
public void pvpmain() {
System.out.println("Player vs Player working!");
ship1.setSize(125, 125);
ship1.setLocation((int) ((win_size / 2) - (ship1.getWidth() / 2)), win_size - ship1.getHeight() - ship_clearence);
ship2.setSize(125, 125);
ship2.setLocation((int) ((win_size / 2) - (ship1.getWidth() / 2)), 0);
window.add(ship1, 0);
window.add(ship2, 0);
window.repaint();
timer = new Timer(10, this);
timer.start();
}
public void move() {
if (dir1 == left1) {
if (ship1.getX() <= speed) {
dir1 = stop1;
} else {
ship1.setLocation(ship1.getX() - speed, ship1.getY());
}
} else if (dir1 == right1) {
if (ship1.getX() + ship1.getWidth() >= win_size - speed) {
dir1 = stop1;
} else {
ship1.setLocation(ship1.getX() + speed, ship1.getY());
}
}
if (dir2 == left2) {
if (ship2.getX() <= speed) {
dir2 = stop2;
} else {
ship2.setLocation(ship2.getX() - speed, ship2.getY());
}
} else if (dir2 == right2) {
if (ship2.getX() + ship2.getWidth() >= win_size - speed) {
dir2 = stop2;
} else {
ship2.setLocation(ship2.getX() + speed, ship2.getY());
}
}
}
public void actionPerformed(ActionEvent e) {
if (e.getSource() == timer) {
System.out.println("Timer Working!");
move();
}
}
}
但是当你释放钥匙时,玩家不会停止移动...
嗯,是的,这是真的,但原始代码似乎没有那个功能
所以,在PlayervsPlayer
构造函数中,我们用更像......的内容替换现有的绑定...
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "player1-left-pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "player1-right-pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), "player2-left-pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_E, 0, false), "player2-right-pressed");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, true), "player1-left-released");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, true), "player1-right-released");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "player2-left-released");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_E, 0, true), "player2-right-released");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "space");
// I'd prefer this to be a self containted unit of work, but for demonstration purposes
actionMap.put("player1-left-pressed", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 1 - left");
dir1 = left1;
}
});
actionMap.put("player1-right-pressed", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 1 - right");
dir1 = right1;
}
});
actionMap.put("player2-left-pressed", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 2 - left");
dir2 = left2;
}
});
actionMap.put("player2-right-pressed", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 2 - right");
dir2 = right2;
}
});
actionMap.put("player1-left-released", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 1 - left");
dir1 = stop1;
}
});
actionMap.put("player1-right-released", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 1 - stop");
dir1 = stop1;
}
});
actionMap.put("player2-left-released", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 2 - stop");
dir2 = stop2;
}
});
actionMap.put("player2-right-released", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Player 2 - stop");
dir2 = stop2;
}
});
actionMap.put("space", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Pew pew pew");
}
});
我还添加了 Space ,因为我忘了把它放在上一个例子中
我很想知道密钥绑定对我的代码有多长
我最喜欢的主题 - 减少和重复使用。正如我在上面的评论中所说,我更喜欢有一个可以改变某种状态的“移动动作”类,这里我使用了Set
,但你可以传递一个{{1}的实例。 1}}并让PlayervsPlayer
在其上调用一个方法,告诉类发生了什么操作,我更喜欢这种方法,因为它解耦代码(使其更易于重复使用)
MoveAction