我正在用Swing製作一個老派的Java遊戲。我已經讀過,爲了實時捕獲輸入,我需要在新線程中運行我的遊戲循環,以便它的方法不會干擾輸入捕捉。我做了InputCapture
類實現KeyListener
,我已經實現keyPressed()
方式類似:KeyListener在遊戲輸入捕捉的新線程
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()
方法,當單擊New Game按鈕時調用。此方法創建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
並重新顯示它,但點擊紅色關閉按鈕沒有任何影響。
可能的複製[*與按鍵綁定*線程(https://stackoverflow.com/q/13999506/230513)。 – trashgod
[如何使用密鑰綁定](http://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html) – MadProgrammer