gpt4 book ai didi

java - 新线程中的 KeyListener 用于捕获游戏输入

转载 作者:行者123 更新时间:2023-12-02 02:48:13 25 4
gpt4 key购买 nike

我正在用 Java 用 Swing 制作一个老式的贪吃蛇游戏。我读过,为了实时捕获输入,我需要在新线程中运行游戏循环,以便它的 wait() 方法不会干扰输入捕获。我已经创建了 InputCapture 类来实现 KeyListener 并实现了 keyPressed() 方法,如下所示:

public class InputCapture implements KeyListener {

private Direction capturedDirection;

//Methods
@Override
public void keyPressed(KeyEvent e) {
boolean inputConsoleDebug = true;
if (e.getKeyCode() == KeyEvent.VK_LEFT) {
capturedDirection = Direction.left;
if (inputConsoleDebug) System.out.println("LEFT");
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
capturedDirection = Direction.right;
if (inputConsoleDebug) System.out.println("RIGHT");
} else if (e.getKeyCode() == KeyEvent.VK_UP) {
capturedDirection = Direction.up;
if (inputConsoleDebug) System.out.println("UP");
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
capturedDirection = Direction.down;
if (inputConsoleDebug) System.out.println("DOWN");
}
}

@Override
public void keyReleased(KeyEvent e) {
}

@Override
public void keyTyped(KeyEvent e) {
}

public Direction getCapturedDirection() {
return capturedDirection;
}
}

然后我创建了 Game 类扩展 Thread 并将游戏循环代码放入 run() 方法中:

public class Game extends Thread {

private Board board;
private Snake snake;
private JFrame frame;
private long waitTime;
private int difficultyStep;
private Direction inputDirection;
private InputCapture inputManager;

//Constructors
Game(Dimension boardSize) {
//Set difficulty
int applesToWin = boardSize.width * boardSize.height - 1;
final int easiestWaitTime = 1000;
final int hardestWaitTime = 100;
difficultyStep = (easiestWaitTime - hardestWaitTime) / applesToWin;
waitTime = easiestWaitTime;
//Set starting point
final int startingPointX = boardSize.width / 2;
final int startingPointy = boardSize.height / 2;
//Set board and snake
board = new Board(boardSize);
snake = new Snake(board, startingPointX, startingPointy);
//Set window Frame
frame = new JFrame(SnakeApplication.getApplicationName());
frame.setContentPane(board);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setResizable(false);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
super.windowClosing(e);
interrupt();
}
});
//Set input manager
inputManager = new InputCapture();
frame.addKeyListener(inputManager);
inputDirection = null;
}

//Methods
public void run() {
board.spawnApple();
while (!isWon()) {
try {
sleep(waitTime);
} catch (InterruptedException e) {
return;
}
try {
inputDirection = inputManager.getCapturedDirection();
snake.move(inputDirection);
} catch (LosingMove e) {
showGameOverDialog();
return;
}
board.repaint();
}
showWinDialog();
}

JFrame getFrame() {
return frame;
}

private boolean isWon() {
for (int row = 0; row < board.getFields().length; row++) {
for (int col = 0; col < board.getFields()[0].length; col++) {
if (!(board.getFields()[row][col].getContent() instanceof Snake.SnakeNode)) return false;
}
}
return true;
}

private void showGameOverDialog() {
JFrame gameOverFrame = new JFrame();
JOptionPane.showMessageDialog(gameOverFrame, "Game Over!");
}

private void showWinDialog() {
JFrame gameOverFrame = new JFrame();
JOptionPane.showMessageDialog(gameOverFrame, "You Win!");
}
}

在我的 MainMenu 类中,我创建了 startNewGame() 方法,单击“新游戏”按钮时会调用该方法。此方法创建 Game 对象并通过调用 start() 方法启动一个新线程。

public class MainMenu {

//Form components references
private JButton exitButton;
private JFrame frame;
private JPanel mainPanel;
private JButton newGameButton;
private JLabel titleLabel;

//Constructors
MainMenu() {
//Set window Frame
frame = new JFrame(SnakeApplication.getApplicationName());
frame.setContentPane(mainPanel);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setResizable(false);
frame.pack();
newGameButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
startNewGame();
}
});
exitButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
exitGame();
}
});
}

JFrame getFrame() {
return frame;
}

private Dimension showBoardSizeDialog() {
Frame boardSizeFrame = new Frame();
int width = Integer.parseInt(JOptionPane.showInputDialog(boardSizeFrame, "Set board's width:"));
int height = Integer.parseInt(JOptionPane.showInputDialog(boardSizeFrame, "Set board's height:"));
return new Dimension(width, height);
}

private void startNewGame() {
Dimension boardSize = showBoardSizeDialog();
frame.setVisible(false);
Game game = new Game(boardSize);
game.getFrame().setVisible(true);
//Starting game loop in a new thread
game.start();
try {
game.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
frame.setVisible(true);
}
}

但是在测试应用程序时,它会陷入游戏循环中并且根本不捕获输入。为什么?我试图调试它,但每次启动新线程时,它都会卡在游戏循环中。仅当主线程结束其执行时,才会绘制 Board 本身。为什么?如果执行卡在那里,不应该在游戏循环期间重新绘制很多次吗?

此外,当单击框架的关闭按钮(红色 X 按钮)时,我会中断线程,因此执行可以返回到 MainMenu 并重新出现,但单击红色关闭按钮没有任何效果。

最佳答案

程序由于在 startNewGame 中调用 game.join() 而卡住。 join 阻止调用它的线程继续执行,直到调用它的线程终止。在您的情况下,join 违背了使用另一个线程的目的,因此您应该删除它。

不过,还有其他问题。您可能不应该使用线程。 You should probably use a Swing TimerSwing isn't thread-safe ,而且我已经可以看到您的代码在一些地方也不是线程安全的。 (例如,您需要将 capturedDirection 声明为 volatile 。)使用 Swing 编写正确的多线程代码有点复杂,仅使用计时器会简单得多.

否则,如果您不使用计时器,则需要使用例如游戏线程(写入共享游戏状态)和进行绘画的 Swing 线程(大概从共享游戏状态读取)之间的同步。如果不这样做,您可能会遇到难以诊断的问题。

另请参阅The Use of Multiple JFrames: Good or Bad Practice?

关于java - 新线程中的 KeyListener 用于捕获游戏输入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44244593/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com