<pre name="code" class="java">package com.lovo;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import javax.swing.JFrame;
import javax.swing.Timer;
@SuppressWarnings("serial")
public class GameFrame extends JFrame {
private int total = 0;
private Image offImage = new BufferedImage(GameContext.GAME_SIZE, GameContext.GAME_SIZE, 1);
private Timer timer = null;
private Snake snake = null;
private Egg egg = null;
private boolean acceptNextKey = true;
public GameFrame() {
this.setTitle("贪吃蛇");
this.setSize(GameContext.GAME_SIZE, GameContext.GAME_SIZE);
this.setResizable(false);
this.setLocationRelativeTo(null);
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
save();
System.exit(0);
}
});
this.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (acceptNextKey) {
Direction newDir = snake.getDir();
switch (e.getKeyCode()) {
case KeyEvent.VK_W: //上
newDir = Direction.UP;
break;
case KeyEvent.VK_S: // 下
newDir = Direction.DOWN;
break;
case KeyEvent.VK_A: // 左
newDir = Direction.LEFT;
break;
case KeyEvent.VK_D: // 右
newDir = Direction.RIGHT;
break;
case KeyEvent.VK_F2: // 重新开始
resetGame();
return;
}
if (snake.getDir() != newDir) { // 如果新方向不同于蛇原来的方向
snake.changeDir(newDir); // 调用蛇的changeDir方法修改蛇的方向
acceptNextKey = false;
}
}
}
});
load(); // 读取存档
if(snake == null) { // 没有存档
resetGame();
}
timer = new Timer(GameContext.INTERVAL, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
snake.move(); // 调用蛇的move方法让蛇向前移动
acceptNextKey = true;
if(!snake.eatEgg(egg)) { // 没有吃到蛋
snake.removeTail();
}
else {
total += GameContext.MARK_PER_EGG;
egg = new Egg(); // 重新创建一颗蛋
}
SnakeNode head = snake.getHead();
int x = head.getX();
int y = head.getY();
// 判断蛇有没有咬到自己或者撞到围墙
if(snake.eatSelf() || (x < GameContext.WALL_X ||
x > GameContext.WALL_X + GameContext.SQUARE_SIZE - GameContext.SNAKE_SIZE ||
y < GameContext.WALL_Y ||
y > GameContext.WALL_Y + GameContext.SQUARE_SIZE - GameContext.SNAKE_SIZE)) {
snake.setAlive(false);
timer.stop();
}
else {
repaint();
}
}
});
timer.start();
}
@Override
public void paint(Graphics g) { // 使用双缓冲方式消除窗口闪烁(见下面步骤1-2-3)
// 1. 获得内存中图像的画笔
Graphics offG = offImage.getGraphics();
// 2. 用offG在内存中绘制窗口上的所有内容
super.paint(offG); // 消除残影
Graphics2D g2d = (Graphics2D) offG; // 将Graphics转成其子类型Graphics2D并设置粗细
Stroke oldStroke = g2d.getStroke(); // 记录画笔粗细(保存现场)
g2d.setStroke(new BasicStroke(GameContext.WALL_WIDTH)); // 将画笔加粗
offG.setColor(Color.BLACK); // 设置画笔为黑色
int x = GameContext.WALL_X, y = GameContext.WALL_Y;
offG.drawRect(x, y, GameContext.SQUARE_SIZE, GameContext.SQUARE_SIZE); // 绘制围墙
g2d.setStroke(oldStroke); // 还原画笔粗细(恢复现场)
if(snake != null) snake.draw(offG); // 调用蛇的draw方法绘制蛇
if(egg != null) egg.draw(offG); // 调用蛋的draw方法绘制蛋
offG.setColor(Color.RED);
offG.drawString("分数: " + total, 50, 40); // 绘制得分
// 3. 将内存中绘制好的图直接画到窗口上(双缓冲)
g.drawImage(offImage, 0, 0, null); // 第二个和第三个参数是绘图的位置
}
private void resetGame() {
snake = new Snake();
egg = new Egg();
total = 0;
}
// 读档
private void load() {
File f = new File("game.sav");
if(f.exists()) {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream(f));
GameRecord record = (GameRecord) ois.readObject();
if(record != null) {
snake = record.getSnake();
egg = record.getEgg();
total = record.getScore();
}
}
catch(IOException e) {
e.printStackTrace();
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
finally {
if(ois != null) {
try {
ois.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
// 存档
private void save() {
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("game.sav"));
if(snake != null && snake.isAlive()) {
GameRecord record = new GameRecord();
record.setSnake(snake);
record.setEgg(egg);
record.setScore(total);
oos.writeObject(record);
}
else {
File f = new File("game.sav");
if(f.exists()) {
f.deleteOnExit();
}
}
}
catch(IOException e) {
e.printStackTrace();
}
finally {
if(oos != null) {
try {
oos.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package com.lovo;
import java.awt.Graphics;
import java.io.Serializable;
import java.util.LinkedList;
/**
* 蛇
*
* @author XWJ
*
*/
@SuppressWarnings("serial")
public class Snake implements Serializable {
private Direction dir = Direction.LEFT; // 方向
private LinkedList<SnakeNode> list = new LinkedList<SnakeNode>(); // 装蛇的所有节点的容器(链表)
private boolean alive = true; // 蛇是否活着
/**
* 构造器
*/
public Snake() {
// 初始化5个节点
for (int i = 0; i < GameContext.SNAKE_LENGTH; i++) {
list.add(new SnakeNode(GameContext.SNAKE_X + i
* GameContext.SNAKE_SIZE, GameContext.SNAKE_Y));
}
}
/**
* 改变方向
*
* @param newDir 新的方向
*/
public void changeDir(Direction newDir) {
if (!((dir == Direction.LEFT && newDir == Direction.RIGHT)
|| (dir == Direction.RIGHT && newDir == Direction.LEFT)
|| (dir == Direction.UP && newDir == Direction.DOWN) || (dir == Direction.DOWN && newDir == Direction.UP))) {
dir = newDir;
}
}
/**
* 获得蛇前进的方向
*/
public Direction getDir() {
return dir;
}
/**
* 获得蛇头
*/
public SnakeNode getHead() {
return list.get(0); // 容器中的第一个节点就是蛇头
}
/**
* 吃蛋
*
* @param egg 蛋
* @return 吃到蛋返回true否则返回false
*/
public boolean eatEgg(Egg egg) {
SnakeNode head = getHead();
return head.getX() == egg.getX() && head.getY() == egg.getY();
}
/**
* 蛇有没有咬到自己
*
* @return 咬到自己返回true否则返回false
*/
public boolean eatSelf() {
SnakeNode head = getHead();
for (int i = 1; i < list.size(); i++) {
SnakeNode temp = list.get(i);
if (temp.getX() == head.getX() && temp.getY() == head.getY()) {
return true;
}
}
return false;
}
/**
* 向前移动
*/
public void move() {
SnakeNode head = getHead();
int x = head.getX(), y = head.getY();
int size = GameContext.SNAKE_SIZE;
switch (dir) {
case UP:
y -= size;
break;
case DOWN:
y += size;
break;
case LEFT:
x -= size;
break;
case RIGHT:
x += size;
break;
}
SnakeNode newHead = new SnakeNode(x, y); // 创建新的蛇头节点
list.addFirst(newHead); // 头上加一个节点
}
/**
* 删除蛇尾
*/
public void removeTail() {
list.removeLast();
}
/**
* 绘制蛇
*/
public void draw(Graphics g) {
// 绘制蛇身上的每个节点
for (SnakeNode node : list) {
node.draw(g);
}
}
public boolean isAlive() {
return alive;
}
public void setAlive(boolean alive) {
this.alive = alive;
}
}
package com.lovo;
import java.awt.Color;
import java.awt.Graphics;
import java.io.Serializable;
/**
* 蛇身上的节点
* @author XWJ
*
*/
@SuppressWarnings("serial")
public class SnakeNode implements Serializable {
private int x, y;
private int size = GameContext.SNAKE_SIZE;
/**
* 构造器
*/
public SnakeNode(int x, int y) {
this.x = x;
this.y = y;
}
/**
* 蛇节点的横坐标
*/
public int getX() { return x; }
/**
* 蛇节点的纵坐标
*/
public int getY() { return y; }
/**
* 绘制
*/
public void draw(Graphics g) {
g.setColor(Color.GREEN);
g.fillRect(x, y, size, size);
g.setColor(Color.BLACK);
g.drawRect(x, y, size, size);
}
}
package com.lovo;
import java.io.Serializable;
@SuppressWarnings("serial")
/**
* 游戏存档
* @author XWJ
*
*/
public class GameRecord implements Serializable {
private Snake snake;
private Egg egg;
private int score;
public Snake getSnake() {
return snake;
}
public void setSnake(Snake snake) {
this.snake = snake;
}
public Egg getEgg() {
return egg;
}
public void setEgg(Egg egg) {
this.egg = egg;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
package com.lovo;
public class GameContext {
/**
* 蛇的长度
*/
public static final int SNAKE_LENGTH = 10;
/**
* 蛇头初始位置的横坐标
*/
public static final int SNAKE_X = 270;
/**
* 蛇头初始位置的纵坐标
*/
public static final int SNAKE_Y = 290;
/**
* 蛇节点的大小
*/
public static final int SNAKE_SIZE = 20;
/**
* 游戏窗口的大小
*/
public static final int GAME_SIZE = 600;
/**
* 游戏场地的大小
*/
public static final int SQUARE_SIZE = 500;
/**
* 刷新周期
*/
public static final int INTERVAL = 200;
/**
* 围墙起点的横坐标
*/
public static final int WALL_X = 50;
/**
* 围墙起点的纵坐标
*/
public static final int WALL_Y = 50;
/**
* 围墙厚度
*/
public static final int WALL_WIDTH = 5;
/**
* 蛋的大小
*/
public static final int EGG_SIZE = SNAKE_SIZE;
/**
* 吃一个蛋的得分
*/
public static final int MARK_PER_EGG = 5;
}
package com.lovo;
import java.awt.Color;
import java.awt.Graphics;
import java.io.Serializable;
@SuppressWarnings("serial")
public class Egg implements Serializable {
private int x, y;
private int size = GameContext.EGG_SIZE;
public Egg() {
x = (int)(Math.random() * GameContext.SQUARE_SIZE / GameContext.EGG_SIZE) * GameContext.EGG_SIZE + GameContext.WALL_X;
y = (int)(Math.random() * GameContext.SQUARE_SIZE / GameContext.EGG_SIZE) * GameContext.EGG_SIZE + GameContext.WALL_Y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void draw(Graphics g) {
g.setColor(Color.ORANGE);
g.fillOval(x, y, size, size);
}
}
package com.lovo;
public enum Direction {
UP, DOWN, LEFT, RIGHT
}
package com.lovo;
class GameRunner {
public static void main(String[] args) {
new GameFrame().setVisible(true);
}
}