贪吃蛇

<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);
	}
}



                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值