一个非常简单的贪吃蛇
共4个类:
Main---------------入口类
GameCanvas----主体类
Snake-------------蛇
Food--------------食物
截图:
代码:
类Main:
/**
* 入口类
* @author kyda
*/
public class Main {
public Main() {
}
public static void main(String[] args) {
new GameCanvas();
}
}
类GameCanvas:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
/**
* 游戏主体类
* @author kyda
*/
public class GameCanvas extends JFrame implements Runnable {
//public static final int UP_BORDER = 30, LEFT_BORDER = 10;
public static final int FRAME_WIDTH = 800, FRAME_HEIGHT = 600; //游戏界面大小
private Random rand = new Random();
private Thread thread;
private Snake snake;
private Food food;
private boolean isGameOver = false;
// 双缓冲的成员变量
private Image offScreenImage = null;
private Graphics offScreen = null;
public GameCanvas() {
super("贪吃蛇游戏");
setSize(FRAME_WIDTH, FRAME_HEIGHT);
setLocationRelativeTo(null);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addKeyListener(new GameKeyListener());
init();
repaint();
thread = new Thread(this);
thread.start();
}
/**
* 游戏初始化
*/
public void init() {
snake = new Snake();
createFood();
}
/**
* 随机位置创建食物
*/
public void createFood() {
int x = rand.nextInt(FRAME_WIDTH / Food.FOOD_WIDTH) * Food.FOOD_WIDTH;
int y = rand.nextInt(FRAME_HEIGHT / Food.FOOD_HEIGHT) * Food.FOOD_HEIGHT;
food = new Food(x, y);
}
/**
* 绘制游戏物体
* 使用双缓冲防止闪烁
*/
@Override
public void paint(Graphics g) {
if (offScreenImage == null) {
offScreenImage = this.createImage(FRAME_WIDTH, FRAME_HEIGHT);
}
if (offScreen == null)
offScreen = offScreenImage.getGraphics();
super.paint(offScreen);
Color c = offScreen.getColor();
offScreen.setColor(Color.WHITE);
offScreen.fillRect(0, 0, FRAME_WIDTH, FRAME_HEIGHT);
offScreen.setColor(c);
snake.drawSnake(offScreen);
food.drawFood(offScreen);
offScreen.drawString("Total length: " + snake.body.size(), 10, 50);
if (isGameOver) {
offScreen.setColor(Color.RED);
offScreen.drawString("GameOver! press enter retry!", FRAME_WIDTH / 2 - 70, FRAME_HEIGHT / 2);
}
offScreen.setColor(c);
g.drawImage(offScreenImage, 0, 0, null);
}
/**
* 线程200ms检测碰撞并重画
*/
@Override
public void run() {
while (true) {
if (!isGameOver) {
if (snake.snakeMove(food)) { // 行动中吃到食物
createFood();
}
if (snake.collidedWallOrSelf()) {
isGameOver = true;
}
repaint();
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 内部类,按键监听
* @author kyda
*/
private class GameKeyListener extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
if (snake.direct != Snake.RIGHT)
snake.direct = Snake.LEFT;
break;
case KeyEvent.VK_RIGHT:
if (snake.direct != Snake.LEFT)
snake.direct = Snake.RIGHT;
break;
case KeyEvent.VK_UP:
if (snake.direct != Snake.DOWN)
snake.direct = Snake.UP;
break;
case KeyEvent.VK_DOWN:
if (snake.direct != Snake.UP)
snake.direct = Snake.DOWN;
break;
case KeyEvent.VK_ENTER: // gameover后按enter重来
if (isGameOver) {
isGameOver = false;
init();
}
break;
default:
break;
}
}
}
}
类Snake:
import java.util.*;
import java.awt.Graphics;
/**
* 蛇的类
* @author kyda
*/
public class Snake {
// 四个方向
public static final int LEFT = 0, UP = 1, RIGHT = 2, DOWN = 3;
// 链表储存身体的每个部分,各个部分用Food表示
public List<Food> body = new LinkedList<Food>();
// 蛇行进方向
public int direct;
/**
* 初始化蛇,默认方向向右
*/
public Snake() {
for (int i = 0; i < 3; ++i) {
body.add(new Food((21 - i) * Food.FOOD_WIDTH, 15 * Food.FOOD_HEIGHT));
}
direct = RIGHT;
}
/**
* 画整条蛇,通过依次画出身体每个部分
* @param g
*/
public void drawSnake(Graphics g) {
for (Food food : body) {
food.drawFood(g);
}
}
/**
* 蛇的行走,并判断是否碰到食物
* @param food
* @return 是否吃下食物
*/
public boolean snakeMove(Food food) {
int desx = body.get(0).x;
int desy = body.get(0).y;
switch (direct) {
case LEFT:
desx = body.get(0).x - Food.FOOD_WIDTH;
break;
case RIGHT:
desx = body.get(0).x + Food.FOOD_WIDTH;
break;
case UP:
desy = body.get(0).y - Food.FOOD_HEIGHT;
break;
case DOWN:
desy = body.get(0).y + Food.FOOD_HEIGHT;
break;
}
// 吃下食物则整体不动
if (desx == food.x && desy == food.y) {
eatFood(food);
return true;
}
// 将身体前一部分位置赋给后一部分
for (int i = body.size() - 1; i > 0; --i) {
body.get(i).x = body.get(i - 1).x;
body.get(i).y = body.get(i - 1).y;
}
body.get(0).x = desx;
body.get(0).y = desy;
return false;
}
/**
* 吃下食物,插入链表开头
* @param food
*/
public void eatFood(Food food) {
body.add(0, food);
}
/**
* 判断是否撞墙或吃到自己
* @return 是否撞墙或吃到自己
*/
public boolean collidedWallOrSelf() {
Food head = body.get(0);
if (head.x < 0 || head.x >= GameCanvas.FRAME_WIDTH || head.y < 0
|| head.y >= GameCanvas.FRAME_HEIGHT)
return true;
for (int i = 1; i < body.size(); ++i) {
Food food = body.get(i);
if (head.x == food.x && head.y == food.y)
return true;
}
return false;
}
}
类Food:
import java.awt.*;
/**
* 食物类
* @author kyda
*/
public class Food {
// 食物宽度和高度
public static final int FOOD_WIDTH = 20, FOOD_HEIGHT = 20;
// 食物坐标
public int x, y;
public Food() {
}
public Food(int x, int y) {
this.x = x;
this.y = y;
}
/**
* 画出食物
* @param g
*/
public void drawFood(Graphics g) {
g.drawRect(x, y, FOOD_WIDTH, FOOD_HEIGHT);
}
/**
* 检测是否与Food碰撞
* @param food
* @return 是否与Food碰撞
*/
public boolean isCollideWith(Food food) {
if (this.x == food.x && this.y == food.y) {
return true;
}
return false;
}
}