本文将提供一个
Swing版本的
贪吃蛇游戏,游戏包括最基本的功能:
1. 用Timer来管理贪吃蛇
线程。
2. 实现按钮,键盘的事件响应。
3. 随机产生食物。
4. 游戏结束的判断:蛇头触碰到蛇身或者蛇头触碰到边界。
5. 实现游戏过程中的暂停以及贪吃蛇运行速度调整。
6. … …
程序界面如下:左边是贪吃蛇运行的范围,右边暂时只有分数信息,当蛇吃到食物的时候分数加10.
暂停,调整蛇体运行速度界面如下:
主要的代码如下:
class="java">package my.games.snake.model;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import my.games.snake.contants.SnakeGameConstant;
/**
*
* 贪吃蛇游戏用到的格子类
*
* @author Eric
*
*/
public class Grid implements Serializable {
private static final long serialVersionUID = 5105993927776028563L;
private int x; // x location
private int y; // y location
private Color color; // color for square
public Grid() {
}
public Grid(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
/**
* Draw Grid
*
* @param g2
*/
public void draw(Graphics2D g2) {
int clientX = SnakeGameConstant.SNAKE_GAME_PANEL_LEFT + x
* SnakeGameConstant.GRID_SIZE;
int clientY = SnakeGameConstant.SNAKE_GAME_PANEL_TOP + y
* SnakeGameConstant.GRID_SIZE;
Rectangle2D.Double rect = new Rectangle2D.Double(clientX, clientY,
SnakeGameConstant.GRID_SIZE, SnakeGameConstant.GRID_SIZE);
g2.setPaint(color);
g2.fill(rect);
g2.setPaint(Color.BLACK);
g2.draw(rect);
}
/**
* @return the color
*/
public Color getColor() {
return color;
}
/**
* @param color
* the color to set
*/
public void setColor(Color color) {
this.color = color;
}
/**
* @return the x
*/
public int getX() {
return x;
}
/**
* @param x
* the x to set
*/
public void setX(int x) {
this.x = x;
}
/**
* @return the y
*/
public int getY() {
return y;
}
/**
* @param y
* the y to set
*/
public void setY(int y) {
this.y = y;
}
}
package my.games.snake.model;
import java.awt.Graphics2D;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
import my.games.snake.contants.SnakeGameConstant;
import my.games.snake.enums.Direction;
public class Snake implements Serializable {
private static final long serialVersionUID = -1064622816984550631L;
private List<Grid> list = null;
private Direction direction = Direction.RIGHT;
public Snake() {
this.list = new LinkedList<Grid>();
}
public void changeDirection(Direction direction) {
if (direction.isUpDirection()) {
if (!this.direction.isUpDirection()
&& !this.direction.isDownDirection()) {
this.direction = direction;
}
} else if (direction.isRightDirection()) {
if (!this.direction.isRightDirection()
&& !this.direction.isLeftDirection()) {
this.direction = direction;
}
} else if (direction.isDownDirection()) {
if (!this.direction.isUpDirection()
&& !this.direction.isDownDirection()) {
this.direction = direction;
}
} else if (direction.isLeftDirection()) {
if (!this.direction.isRightDirection()
&& !this.direction.isLeftDirection()) {
this.direction = direction;
}
}
}
public void draw(Graphics2D g2) {
for (Grid grid : list) {
grid.draw(g2);
}
}
/**
* @return the list
*/
public List<Grid> getList() {
return list;
}
/**
* @param list
* the list to set
*/
public void setList(List<Grid> list) {
this.list = list;
}
/**
* @return the direction
*/
public Direction getDirection() {
return direction;
}
/**
* @param direction
* the direction to set
*/
public void setDirection(Direction direction) {
this.direction = direction;
}
public void move() {
Grid currentHead = list.get(0);
int headX = currentHead.getX();
int headY = currentHead.getY();
currentHead.setColor(SnakeGameConstant.SNAKE_BODY_COLOR);
if (direction.isDownDirection()) {
list.add(0, new Grid(headX, headY + 1,
SnakeGameConstant.SNAKE_HEADER_COLOR));
list.remove(list.size() - 1);
} else if (direction.isUpDirection()) {
list.add(0, new Grid(headX, headY - 1,
SnakeGameConstant.SNAKE_HEADER_COLOR));
list.remove(list.size() - 1);
} else if (direction.isRightDirection()) {
list.add(0, new Grid(headX + 1, headY,
SnakeGameConstant.SNAKE_HEADER_COLOR));
list.remove(list.size() - 1);
} else if (direction.isLeftDirection()) {
list.add(0, new Grid(headX - 1, headY,
SnakeGameConstant.SNAKE_HEADER_COLOR));
list.remove(list.size() - 1);
}
}
}
package my.games.snake.ui;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JRadioButtonMenuItem;
import my.games.snake.contants.SnakeGameConstant;
import my.games.snake.enums.GameState;
public class SnakeGameFrame extends JFrame {
private static final long serialVersionUID = 998014032682506026L;
private SnakeGamePanel panel;
private Container contentPane;
private JMenuItem startMI = new JMenuItem("Start");
private JMenuItem pauseMI = new JMenuItem("Pause");
private JMenu speedMenu = new JMenu("Speed");
private JMenuItem exitMI = new JMenuItem("Exit");
private JMenuItem aboutMI = new JMenuItem("About");
private JRadioButtonMenuItem speedMI1 = new JRadioButtonMenuItem("Speed1",
true);
private JRadioButtonMenuItem speedMI2 = new JRadioButtonMenuItem("Speed2",
false);
private JRadioButtonMenuItem speedMI3 = new JRadioButtonMenuItem("Speed3",
false);
private JRadioButtonMenuItem speedMI4 = new JRadioButtonMenuItem("Speed4",
false);
private JRadioButtonMenuItem speedMI5 = new JRadioButtonMenuItem("Speed5",
false);
public int speedFlag = 1;
public SnakeGameFrame() {
setTitle(SnakeGameConstant.SNAKE_GAME);
setSize(SnakeGameConstant.SNAKE_GAME_FRAME_WIDTH,
SnakeGameConstant.SNAKE_GAME_FRAME_HEIGHT);
setResizable(false);
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu setMenu = new JMenu("Set");
JMenu helpMenu = new JMenu("Help");
setMenu.setMnemonic('s');
setMenu.setMnemonic('H');
menuBar.add(setMenu);
menuBar.add(helpMenu);
setMenu.add(startMI);
setMenu.add(pauseMI);
setMenu.addSeparator();
setMenu.addSeparator();
setMenu.add(speedMenu);
setMenu.addSeparator();
setMenu.add(exitMI);
ButtonGroup group = new ButtonGroup();
group.add(speedMI1);
group.add(speedMI2);
group.add(speedMI3);
group.add(speedMI4);
group.add(speedMI5);
speedMenu.add(speedMI1);
speedMenu.add(speedMI2);
speedMenu.add(speedMI3);
speedMenu.add(speedMI4);
speedMenu.add(speedMI5);
startMI.addActionListener(new StartAction());
pauseMI.addActionListener(new PauseAction());
exitMI.addActionListener(new ExitAction());
speedMI1.addActionListener(new SpeedAction());
speedMI2.addActionListener(new SpeedAction());
speedMI3.addActionListener(new SpeedAction());
speedMI4.addActionListener(new SpeedAction());
speedMI5.addActionListener(new SpeedAction());
helpMenu.add(aboutMI);
aboutMI.addActionListener(new AboutAction());
contentPane = getContentPane();
panel = new SnakeGamePanel(this);
contentPane.add(panel);
startMI.setEnabled(true);
pauseMI.setEnabled(false);
// 设置游戏状态是初始化状态
panel.setGameState(GameState.INITIALIZE);
}
private class StartAction implements ActionListener {
public void actionPerformed(ActionEvent event) {
startMI.setEnabled(false);
pauseMI.setEnabled(true);
panel.setGameState(GameState.RUN);
panel.getTimer().start();
}
}
private class PauseAction implements ActionListener {
public void actionPerformed(ActionEvent event) {
pauseMI.setEnabled(false);
startMI.setEnabled(true);
panel.setGameState(GameState.PAUSE);
if (panel.getTimer().isRunning()) {
panel.getTimer().stop();
}
}
}
private class SpeedAction implements ActionListener {
public void actionPerformed(ActionEvent event) {
Object speed = event.getSource();
if (speed == speedMI1) {
speedFlag = 1;
} else if (speed == speedMI2) {
speedFlag = 2;
} else if (speed == speedMI3) {
speedFlag = 3;
} else if (speed == speedMI4) {
speedFlag = 4;
} else if (speed == speedMI5) {
speedFlag = 5;
}
panel.getTimer().setDelay(1000 - 200 * (speedFlag - 1));
}
}
private class ExitAction implements ActionListener {
public void actionPerformed(ActionEvent event) {
int result = JOptionPane.showConfirmDialog(SnakeGameFrame.this,
SnakeGameConstant.QUIT_GAME, SnakeGameConstant.SNAKE_GAME,
JOptionPane.YES_NO_OPTION);
if (result == JOptionPane.YES_OPTION) {
System.exit(0);
}
}
}
private class AboutAction implements ActionListener {
public void actionPerformed(ActionEvent event) {
String string = SnakeGameConstant.KEYBOARDS_DESCRIPTION;
JOptionPane.showMessageDialog(SnakeGameFrame.this, string);
}
}
}
package my.games.snake.ui;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.Timer;
import my.games.snake.contants.SnakeGameConstant;
import my.games.snake.enums.Direction;
import my.games.snake.enums.GameState;
import my.games.snake.model.Grid;
import my.games.snake.model.Snake;
public class SnakeGamePanel extends JPanel {
private static final long serialVersionUID = -4173775119881265176L;
private int flag[][] = new int[SnakeGameConstant.GRID_COLUMN_NUMBER][SnakeGameConstant.GRID_ROW_NUMBER];// 在一个20*30的界面中,设置每个方块的flag
private Color color[][] = new Color[SnakeGameConstant.GRID_COLUMN_NUMBER][SnakeGameConstant.GRID_ROW_NUMBER];// 在一个20*30的界面中,设置每个方块的颜色
private Snake snake;
private Grid food;
public TimerAction timerAction;
private int score;
private SnakeGameFrame frame;
private Timer timer;
private Grid grid;
private GameState gameState = GameState.INITIALIZE;
//private GameOverType gameOverType = GameOverType.TOUCH_EDGE;
private boolean needToGenerateFood = false;
public SnakeGamePanel(SnakeGameFrame frame) {
for (int i = SnakeGameConstant.LEFT; i <= SnakeGameConstant.RIGHT; i++) {
for (int j = SnakeGameConstant.UP; j <= SnakeGameConstant.DOWN; j++) {
flag[i][j] = 0;
}
}
addKeyListener(new KeyHandler());
setFocusable(true);
init();
timerAction = new TimerAction();
timer = new Timer(1000, timerAction);
score = 0;
this.frame = frame;
grid = new Grid();
}
private void init() {
initSnake();
initFood();
}
private void initSnake() {
snake = new Snake();
List<Grid> list = new LinkedList<Grid>();
list.add(new Grid(4, 1, SnakeGameConstant.SNAKE_BODY_COLOR));
list.add(0, new Grid(5, 1, SnakeGameConstant.SNAKE_HEADER_COLOR));
snake.setList(list);
}
private void initFood() {
food = new Grid();
needToGenerateFood = true;
this.generateFoodByRandom();
}
public void setGameState(GameState state) {
gameState = state;
}
private void judgeGameOver() {
if (isSnakeHeadTouchEdge() || isSnakeHeadTouchBody()) {
gameState = GameState.OVER;
int result = JOptionPane.showConfirmDialog(frame,
SnakeGameConstant.GAME_OVER, SnakeGameConstant.SNAKE_GAME,
JOptionPane.YES_NO_OPTION);
if (result == JOptionPane.YES_OPTION) {
for (int i = SnakeGameConstant.LEFT; i <= SnakeGameConstant.RIGHT; i++) {
for (int j = SnakeGameConstant.UP; j <= SnakeGameConstant.DOWN; j++) {
flag[i][j] = 0;
}
}
gameState = GameState.RUN;
score = 0;
init();
timer.start();
} else {
System.exit(0);
}
}
}
public void drawGameFrame(Graphics2D g2) {
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.draw(new Rectangle2D.Double(SnakeGameConstant.SNAKE_GAME_PANEL_LEFT,
SnakeGameConstant.SNAKE_GAME_PANEL_TOP,
SnakeGameConstant.SNAKE_GAME_PANEL_RIGHT,
SnakeGameConstant.SNAKEGAME_PANEL_BOTTOM));
if (gameState.isInitializedState()) {
return;
}
draw(g2);
drawScore(g);
}
private void draw(Graphics2D g2) {
drawSnake(g2);
drawFood(g2);
for (int i = SnakeGameConstant.LEFT; i <= SnakeGameConstant.RIGHT; i++) {
for (int j = SnakeGameConstant.UP; j <= SnakeGameConstant.DOWN; j++) {
if (flag[i][j] == 1) {
grid.setX(i);
grid.setY(j);
grid.setColor(color[i][j]);
grid.draw(g2);
}
}
}
}
private void drawScore(Graphics g) {
g.drawString("Score: " + score,
SnakeGameConstant.SNAKE_GAME_PANEL_RIGHT + 20, 200);
}
private void drawSnake(Graphics2D g2) {
snake.draw(g2);
}
private void drawFood(Graphics2D g2) {
food.draw(g2);
}
private class KeyHandler implements KeyListener {
public void keyPressed(KeyEvent event) {
if (!gameState.isRunState()) {
return;
}
int keyCode = event.getKeyCode();
switch (keyCode) {
case KeyEvent.VK_LEFT:
snake.changeDirection(Direction.LEFT);
break;
case KeyEvent.VK_RIGHT:
snake.changeDirection(Direction.RIGHT);
break;
case KeyEvent.VK_UP:
snake.changeDirection(Direction.UP);
break;
case KeyEvent.VK_DOWN:
snake.changeDirection(Direction.DOWN);
break;
default:
break;
}
repaint();
}
public void keyReleased(KeyEvent event) {
}
public void keyTyped(KeyEvent event) {
}
}
private class TimerAction implements ActionListener, Serializable {
private static final long serialVersionUID = 749074368549207272L;
public void actionPerformed(ActionEvent e) {
if (!gameState.isRunState()) {
return;
}
generateFoodByRandom();
snake.move();
eatFood();
judgeGameOver();
repaint();
}
}
private boolean isFoodAvailable(int x, int y) {
for (Grid grid : snake.getList()) {
if (x == grid.getX() && y == grid.getY()) {
return false;
}
}
return true;
}
private void generateFoodByRandom() {
if (needToGenerateFood) {
Random r = new Random();
int randomX = r.nextInt(SnakeGameConstant.GRID_COLUMN_NUMBER);
int randomY = r.nextInt(SnakeGameConstant.GRID_ROW_NUMBER);
if (isFoodAvailable(randomX, randomX)) {
food = new Grid(randomX, randomY, SnakeGameConstant.FOOD_COLOR);
needToGenerateFood = false;
} else {
generateFoodByRandom();
}
}
}
private boolean isSnakeHeadTouchEdge() {
Grid head = this.snake.getList().get(0);
if ((head.getX() >= SnakeGameConstant.GRID_COLUMN_NUMBER)
|| (head.getX() < 0)) {
//this.gameOverType = GameOverType.TOUCH_EDGE;
return true;
}
if ((head.getY() >= SnakeGameConstant.GRID_ROW_NUMBER)
|| (head.getY() < 0)) {
//this.gameOverType = GameOverType.TOUCH_EDGE;
return true;
}
return false;
}
private boolean isSnakeHeadTouchBody() {
Grid head = this.snake.getList().get(0);
int length = snake.getList().size();
for (int i = 1; i < length; i++) {
if (head.getX() == snake.getList().get(i).getX()
&& head.getY() == snake.getList().get(i).getY()) {
//this.gameOverType = GameOverType.TOUCH_BODY;
return true;
}
}
return false;
}
private boolean isFoodTouched() {
Grid head = snake.getList().get(0);
return head.getX() == food.getX() && head.getY() == food.getY();
}
private void eatFood() {
if (isFoodTouched()) {
Grid tail = snake.getList().get(snake.getList().size() - 1);
snake.getList().add(tail);
this.needToGenerateFood = true;
this.score += 10;
}
}
/**
* @return the timer
*/
public Timer getTimer() {
return timer;
}
/**
* @param timer
* the timer to set
*/
public void setTimer(Timer timer) {
this.timer = timer;
}
}
完整的代码,请参考附件MySnakeGame.7z,有需要的朋友可以下载。
后续的博文将添加如下功能:
(二)添加随机障碍物。
(三)添加游戏进度的存储和读取
(四)完成游戏排行榜
... ...
- 大小: 20.3 KB
- 大小: 23 KB
- 大小: 20.3 KB
- MySnakeGame.7z (14 KB)
- 下载次数: 1