Your effort to apply the help you've been given is dismal, but your question is interesting and there haven't been many of those lately. The code below is to show that it can be done, and it shows how to do it, maintaining (somewhat) the structure of the code you started with. However, I have eliminated without replacement probably the most significant barrier to achieving your goal, the while( true ) loop. I suggested you replace that with a javax.swing.Timer. Have you done that yet? I'm not saying my example is perfect. Perfect takes a little longer.
As always, keep coding, and good luck!
Instructions: Select "Play" in the Snake Game Control interface, and then type any key when the Game window appears. Check the console output for verification that a keypress was detected.
A class to start the game off right:
import javax.swing.SwingUtilities;
// class SNakeGame creates an instance of GameControl on the EDT which
// presents game control panel interface
public class SnakeGame
{
// the main method launches the game control panel on the EDT
public static void main( String[] args )
{
SwingUtilities.invokeLater( new Runnable()
{
public void run()
{
new GameControl();
}
} );
} // end method main()
} // end class SnakeGame
The GameControl class that presents the interface you've tried to add:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
// class GameControl presents a JFrame
public class GameControl
{
// the default constructor
public GameControl()
{
// set the dialog's basic characteristics
JFrame gameControlDialog = new JFrame();
gameControlDialog.setTitle( "Snake Game Control" );
gameControlDialog.setDefaultCloseOperation( JDialog.EXIT_ON_CLOSE );
gameControlDialog.setModalExclusionType( null );
gameControlDialog.setPreferredSize( new Dimension( 400, 65 ) );
gameControlDialog.add( GameControlButtonPanel() );
gameControlDialog.pack();
gameControlDialog.setVisible( true );
} // end default constructor
// method GameControlButtonPanel returns a JPanel with the buttons
// needed to control the game
private JPanel GameControlButtonPanel()
{
JButton playGame = new JButton( "Play" );
JButton pauseGame = new JButton( "Pause" );
JButton resetGame = new JButton( "Reset" );
JButton quitGame = new JButton( "Quit" );
playGame.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
new SnakeEngine();
} // end actionPerformed() method
} );
pauseGame.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
} // end actionPerformed() method
} );
resetGame.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
} // end actionPerformed() method
} );
quitGame.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
} // end actionPerformed() method
} );
JPanel gameButtonPanel = new JPanel();
gameButtonPanel.add( playGame );
gameButtonPanel.add( pauseGame );
gameButtonPanel.add( resetGame );
gameButtonPanel.add( quitGame );
return gameButtonPanel;
} // end method GameControlButtonPanel()
} // end class GameControl
A collection of the classes from the tutorial you followed, modified:
import java.awt.Color;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.LinkedList;
import javax.swing.JDialog;
import javax.swing.JPanel;
public class SnakeEngine implements KeyListener
{
private static final int UPDATES_PER_SECOND = 10;
private static final Font FONT_SMALL = new Font("Times New Roman",
Font.BOLD, 20);
private static final Font FONT_LARGE = new Font("Times New Roman",
Font.BOLD, 40);
private JDialog snakeDialog;
private DrawPanel panel;
private SnakeBoard board;
private SnakeContents snake;
private int score;
private boolean gameOver;
public SnakeEngine()
{
this.board = new SnakeBoard();
this.snake = new SnakeContents( board );
snakeDialog = new JDialog();
snakeDialog.setTitle( "Snake Game" );
snakeDialog.setModalityType( Dialog.ModalityType.MODELESS );
snakeDialog.setDefaultCloseOperation( JDialog.DISPOSE_ON_CLOSE );
snakeDialog.setResizable( false );
this.panel = new DrawPanel();
panel.setBackground(Color.BLACK);
panel.setPreferredSize(new Dimension(SnakeBoard.MAP_SIZE *
SnakeBoard.TILE_SIZE, SnakeBoard.MAP_SIZE * SnakeBoard.TILE_SIZE));
snakeDialog.add(panel);
snakeDialog.pack();
snakeDialog.setLocationRelativeTo(null);
resetGame();
panel.addKeyListener(this);
System.out.println( "Panel is focusable = " + panel.isFocusable() );
panel.requestFocus();
update();
snakeDialog.setVisible( true );
}
private void update() {
if( gameOver || !panel.hasFocus() )
{
System.out.println( "Returning" );
return;
}
SnakeBoard.TileType snakeTile = snake.updateSnake();
if(snakeTile == null || snakeTile.equals(SnakeBoard.TileType.SNAKE)) {
gameOver = true;
} else if(snakeTile.equals(SnakeBoard.TileType.FRUIT)) {
score += 10;
spawnFruit();
}
}
private void resetGame() {
board.resetBoard();
snake.resetSnake();
score = 0;
gameOver = false;
spawnFruit();
}
private void spawnFruit() {
int random = (int)(Math.random() * ((SnakeBoard.MAP_SIZE *
SnakeBoard.MAP_SIZE) - snake.getSnakeLength()));
int emptyFound = 0;
int index = 0;
while(emptyFound < random) {
index++;
if(board.getTile(index % SnakeBoard.MAP_SIZE, index /
SnakeBoard.MAP_SIZE).equals(SnakeBoard.TileType.EMPTY))
{
emptyFound++;
}
}
board.setTile(index % SnakeBoard.MAP_SIZE, index /
SnakeBoard.MAP_SIZE, SnakeBoard.TileType.FRUIT);
}
@Override
public void keyPressed(KeyEvent e)
{
System.out.println( "A key was pressed." );
}
class DrawPanel extends JPanel
{
public void paintComponent( Graphics g )
{
super.paintComponent( g );
g.setColor(Color.WHITE);
if(gameOver) {
g.setFont(FONT_LARGE);
String message = new String("Final Score: " + score);
g.drawString(message, panel.getWidth() / 2 -
(g.getFontMetrics().stringWidth(message) / 2), 250);
g.setFont(FONT_SMALL);
message = new String("Press Enter to Restart");
g.drawString(message, panel.getWidth() / 2 -
(g.getFontMetrics().stringWidth(message) / 2), 350);
} else {
g.setFont(FONT_SMALL);
g.drawString("Score:" + score, 10, 20);
}
}
} // end class DrawPanel
@Override
public void keyTyped( KeyEvent e )
{
// end method
}
@Override
public void keyReleased( KeyEvent e )
{
// end method
}
}
class SnakeContents
{
public static enum Direction
{
UP,
DOWN,
LEFT,
RIGHT,
NONE
}
private Direction currentDirection;
private Direction temporaryDirection;
private SnakeBoard board;
private LinkedList<Point> points;
public SnakeContents(SnakeBoard board) {
this.board = board;
this.points = new LinkedList<Point>();
}
public void resetSnake() {
this.currentDirection = Direction.NONE;
this.temporaryDirection = Direction.NONE;
Point head = new Point(SnakeBoard.MAP_SIZE / 2, SnakeBoard.MAP_SIZE / 2);
points.clear();
points.add(head);
board.setTile(head.x, head.y, SnakeBoard.TileType.SNAKE);
}
public void setDirection(Direction direction) {
if(direction.equals(Direction.UP) && currentDirection.equals(Direction.DOWN)) {
return;
} else if(direction.equals(Direction.DOWN) && currentDirection.equals(Direction.UP)) {
return;
} else if(direction.equals(Direction.LEFT) && currentDirection.equals(Direction.RIGHT)) {
return;
} else if(direction.equals(Direction.RIGHT) && currentDirection.equals(Direction.LEFT)) {
return;
}
this.temporaryDirection = direction;
}
public SnakeBoard.TileType updateSnake() {
this.currentDirection = temporaryDirection;
Point head = points.getFirst();
switch(currentDirection) {
case UP:
if(head.y <= 0) {
return null;
}
points.push(new Point(head.x, head.y - 1));
break;
case DOWN:
if(head.y >= SnakeBoard.MAP_SIZE - 1) {
return null;
}
points.push(new Point(head.x, head.y + 1));
break;
case LEFT:
if(head.x <= 0) {
return null;
}
points.push(new Point(head.x - 1, head.y));
break;
case RIGHT:
if(head.x >= SnakeBoard.MAP_SIZE - 1) {
return null;
}
points.push(new Point(head.x + 1, head.y));
break;
case NONE:
return SnakeBoard.TileType.EMPTY;
}
head = points.getFirst();
SnakeBoard.TileType oldType = board.getTile(head.x, head.y);
if(!oldType.equals(SnakeBoard.TileType.FRUIT)) {
Point last = points.removeLast();
board.setTile(last.x, last.y, SnakeBoard.TileType.EMPTY);
oldType = board.getTile(head.x, head.y);
}
board.setTile(head.x, head.y, SnakeBoard.TileType.SNAKE);
return oldType;
}
public int getSnakeLength() {
return points.size();
}
public Direction getCurrentDirection() {
return currentDirection;
}
}
class SnakeBoard
{
public static final int TILE_SIZE = 25;
public static final int MAP_SIZE = 20;
public static enum TileType {
SNAKE(Color.GREEN),
FRUIT(Color.RED),
EMPTY(null);
private Color tileColor;
private TileType(Color color) {
this.tileColor = color;
}
public Color getColor() {
return tileColor;
}
}
private TileType[] tiles;
public SnakeBoard() {
tiles = new TileType[MAP_SIZE * MAP_SIZE];
}
public void resetBoard() {
for(int i = 0; i < tiles.length; i++) {
tiles[i] = TileType.EMPTY;
}
}
public void setTile(int x, int y, TileType type) {
tiles[y * MAP_SIZE + x] = type;
}
public TileType getTile(int x, int y) {
return tiles[y * MAP_SIZE + x];
}
public void draw(Graphics2D g) {
g.setColor(TileType.SNAKE.getColor());
for(int i = 0; i < tiles.length; i++) {
int x = i % MAP_SIZE;
int y = i / MAP_SIZE;
if(tiles[i].equals(TileType.EMPTY)) {
continue;
}
if(tiles[i].equals(TileType.FRUIT)) {
g.setColor(TileType.FRUIT.getColor());
g.fillOval(x * TILE_SIZE + 4, y * TILE_SIZE + 4, TILE_SIZE - 8, TILE_SIZE - 8);
g.setColor(TileType.SNAKE.getColor());
} else {
g.fillRect(x * TILE_SIZE + 1, y * TILE_SIZE + 1, TILE_SIZE - 2, TILE_SIZE - 2);
}
}
}
}