使用Java中Frame来实现一个简单的贪吃蛇小游戏,游戏中使用了线程、内部类、双向链表等。
主要包含四个类:
Snake:生成蛇、蛇的移动、添加、碰撞判断
Egg:生成一个食物、画出食物
Yard:生成一个窗体、实现贪吃蛇的主要功能
Dir:四个方向,上下左右
实现效果图:
Snake.java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.util.Map;
/**
*
* @Description
* @author CCQ
* @date 2017年6月22日 下午5:22:03
*
*/
public class Snake {
private Node head = null; //蛇头
private Node tail = null; //蛇尾
private int size = 0; //蛇长度
private Node n = new Node(20,20, Dir.L); //初始化蛇的位子
private Yard y;
public Snake(Yard y) {
head = n;
tail = n;
size = 1;
this.y = y;
}
/**
* 加一段在尾巴上
*/
public void addToTail() {
Node node = null;
switch (tail.dir) {
case L:
node = new Node(tail.row, tail.col + 1, tail.dir);
break;
case U:
node = new Node(tail.row + 1, tail.col, tail.dir);
break;
case R:
node = new Node(tail.row, tail.col - 1, tail.dir);
break;
case D:
node = new Node(tail.row - 1, tail.col, tail.dir);
break;
}
tail.next = node;
node.prev = tail;
tail = node;
size++;
}
/**
* 加一段在头上
*/
public void addHead() {
Node node = null;
switch (head.dir) {
case L:
node = new Node(head.row, head.col - 1, head.dir);
break;
case U:
node = new Node(head.row - 1, head.col, head.dir);
break;
case R:
node = new Node(head.row, head.col + 1, head.dir);
break;
case D:
node = new Node(head.row + 1, head.col, head.dir);
break;
}
node.next = head;
head.prev = node;
head = node;
size++;
}
//画出一条蛇
public void drow(Graphics g) {
if (size <= 0) {
return;
}
move();
for (Node n = head; n != null; n = n.next) {
n.drow(g);
}
}
//蛇移动
public void move() {
addHead();
deleteFromTail();
checkDead();
}
//检查是否碰撞
public void checkDead() {
if (head.row <3 || head.col < 1 || head.row > Yard.ROWS - 2 || head.col > Yard.COLS - 2) {
y.stop();
}
for (Node n = head.next; n != null; n = n.next) {
if (head.row == n.row && head.col == n.col) {
y.stop();
}
}
}
//删出蛇尾
public void deleteFromTail() {
if (size == 0)
return;
tail = tail.prev;
tail.next = null;
}
//定义一个内部类,实现蛇的生成,双向链表
private class Node {
int w = Yard.BLOCK_SIZE;
int h = Yard.BLOCK_SIZE;
int row, col;
Dir dir = Dir.L;
Node next = null; //链表,后一个指针
Node prev = null; //链表,前一个指针
public Node(int row, int col, Dir dir) {
super();
this.row = row;
this.col = col;
this.dir = dir;
}
void drow(Graphics g) {
Color c = g.getColor();
g.setColor(Color.BLACK);
g.fillRect(Yard.BLOCK_SIZE * col, Yard.BLOCK_SIZE * row, w, h);
g.setColor(c);
}
}
//吃食物
public void eat(Egg e) {
if (this.getRect().intersects(e.getRect())) {
boolean isSuccess = false;
while(!isSuccess){
Map map = e.reAppear();
for(Node n = head;n!=null;n=n.next){
if(n.row != map.get("row") || n.col != map.get("col")){
isSuccess = true;
}
}
}
this.addToTail();
y.setScore(y.getScore() + 5);
}
}
public Rectangle getRect() {
return new Rectangle(Yard.BLOCK_SIZE * head.col, Yard.BLOCK_SIZE * head.row, head.w, head.h);
}
// 键盘事件,控制蛇的方向
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
switch (key) {
case KeyEvent.VK_LEFT:
if (head.dir != Dir.R)
head.dir = Dir.L;
break;
case KeyEvent.VK_UP:
if (head.dir != Dir.D)
head.dir = Dir.U;
break;
case KeyEvent.VK_RIGHT:
if (head.dir != Dir.L)
head.dir = Dir.R;
break;
case KeyEvent.VK_DOWN:
if (head.dir != Dir.U)
head.dir = Dir.D;
break;
}
}
}
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
/**
*
* @Description 生成食物
* @author CCQ
* @date 2017年6月23日 上午9:23:59
*
*/
public class Egg {
int row, col;
int w = Yard.BLOCK_SIZE;
int h = Yard.BLOCK_SIZE;
private static Random r = new Random();
public Egg(int row, int col) {
super();
this.row = row;
this.col = col;
}
public Egg() {
this(r.nextInt(Yard.ROWS - 5) +3, r.nextInt(Yard.COLS - 3) +2);
}
//重新设计生成食物
public Map reAppear() {
this.row = r.nextInt(Yard.ROWS - 5) +3;
this.col = r.nextInt(Yard.COLS - 3) +2;
Map map = new HashMap();
map.put("row", this.row);
map.put("col", this.col);
return map;
}
public Rectangle getRect() {
return new Rectangle(Yard.BLOCK_SIZE * this.col, Yard.BLOCK_SIZE * this.row, this.w, this.h);
}
//画出食物
public void drow(Graphics g) {
Color c = g.getColor();
g.setColor(Color.GREEN);
g.fillOval(Yard.BLOCK_SIZE * col, Yard.BLOCK_SIZE * row, w, h);
g.setColor(c);
}
}
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
/**
*
* @Description
* @author CCQ
* @date 2017年6月23日 下午6:24:06
*
*/
public class Yard extends Frame {
private static final long serialVersionUID = 1L;
public static final int ROWS = 30; //定义行数
public static final int COLS = 30; //定义列数
public static final int BLOCK_SIZE = 15;
private Font fontGameOver = new Font("宋体", Font.BOLD, 50);
private int score= 0; //计算获得分数
private boolean gameOver = false; //是否死亡
Snake s = new Snake(this);
Egg e = new Egg();
Image offScreenImage = null;
PaintThread paintThread = new PaintThread();
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
/**
* 生成一个窗口
*/
public void launch() {
this.setLocation(450, 150);
this.setSize(COLS * BLOCK_SIZE, ROWS * BLOCK_SIZE);
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
this.setVisible(true);
this.addKeyListener(new KeyMonitor());
new Thread(paintThread).start();
}
//解决窗口闪烁
@Override
public void update(Graphics g) {
if (offScreenImage == null) {
offScreenImage = this.createImage(COLS * BLOCK_SIZE, ROWS * BLOCK_SIZE);
}
Graphics goff = offScreenImage.getGraphics();
paint(goff);
g.drawImage(offScreenImage, 0, 0, null);
}
//停止
public void stop() {
this.gameOver = true;
}
//画出方格
@Override
public void paint(Graphics g) {
Color c = g.getColor();
g.setColor(Color.GRAY);
g.fillRect(0, 0, COLS * BLOCK_SIZE, ROWS * BLOCK_SIZE);
g.setColor(Color.DARK_GRAY);
// 画出横线
for (int i = 3; i <= ROWS -1; i++) {
g.drawLine(BLOCK_SIZE, BLOCK_SIZE * i, (COLS - 1) * BLOCK_SIZE, BLOCK_SIZE * i);
}
// 画竖线
for (int j = 1; j <= COLS -1; j++) {
g.drawLine(BLOCK_SIZE * j, BLOCK_SIZE * 3, BLOCK_SIZE * j, (ROWS - 1)* BLOCK_SIZE);
}
g.setColor(Color.YELLOW);
g.drawString("分数:"+score+" 按F5可以重新开始", 10, 40);
if(gameOver){
g.setColor(Color.RED);
g.setFont(fontGameOver);
g.drawString("GAME OVER",85, 225);
paintThread.pause();
}
g.setColor(c);
s.eat(e);
e.drow(g);
s.drow(g);
}
private class PaintThread implements Runnable {
private boolean pause = false;
private boolean running = true;
@Override
public void run() {
while (running) {
try {
if(pause) continue;
else repaint();
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void pause() {
this.pause = true;
}
public void reStart() {
this.pause = false;
new Yard().launch();
gameOver = false;
}
}
//绑定键盘事件
private class KeyMonitor extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if(key == KeyEvent.VK_F5) {
paintThread.reStart();
}
s.keyPressed(e);
}
}
public static void main(String[] args) {
new Yard().launch();
}
}
/**
*
* @Description 蛇的方向
* @author CCQ
* @date 2017年6月22日 下午5:33:05
*
*/
public enum Dir {
//左 上 右 下
L,U,R,D
}