package com.zjgs.swing;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class Move {//小蛇或食物位置(方位)移动
int move_X;
int move_Y;
Move(int move_X, int move_Y) {//带参数构造方法进行初始化
this.move_X = move_X;
this.move_Y = move_Y;
}
}
class MoveOperate extends Observable implements Runnable {//小蛇和食物移动操作的类
public static final int LEFT = 1;//小蛇向左移动的标识
public static final int UP = 2;//小蛇向上运动的标识
public static final int RIGHT = 3;//小蛇向右运动的标识
public static final int DOWN = 4;//小蛇向下运动的标识
private boolean[][] isHave; //指示位置上是否有小蛇或食物
private LinkedList snake = new LinkedList();//声明小蛇的双链表集合
private Move aliment; //食物
private int move_Direction = LEFT;//小蛇移动的方向向左
private boolean running = false; // 运行状态
private int timeSpace = 300; //时间间隔
private double speedChange = 0.75; // 每次的速度变化率
private boolean paused = false; // 暂停标志
private int score = 0; //得分
private int moveCount = 0; //吃到食物前移动的次数
private int X;//横坐标
private int Y;//纵坐标
public MoveOperate(int X, int Y) {//带参数构造方法进行初始化
this.X = X;
this.Y = Y;
resetGame();
}
public void run() {//实现Runnable接口必须实现的方法
running = true;//标识设置为真
while (running) {//根据标识进行循环
try {
Thread.sleep(timeSpace);//休眠0.3秒
} catch (Exception e) {
System.out.println("休眠出错:"+e.getMessage());
break;//跳出循环
}
if (!paused) {//小蛇正在移动时
if (move()) {
setChanged();//更新界面数据
notifyObservers();
} else {//弹出对话框显示游戏结束
JOptionPane.showMessageDialog(null, "你失败了!",
"Game Over", JOptionPane.INFORMATION_MESSAGE);
break;//跳出循环
}
}
}
if (!running) {//小蛇暂停或停止
JOptionPane.showMessageDialog(null, "你停止了游戏",
"Game Over", JOptionPane.INFORMATION_MESSAGE);
}
}
public void resetGame() {//重置游戏
move_Direction = MoveOperate.LEFT;//小蛇移动的方向向左
timeSpace = 300;//时间间隔为0.3秒
paused = false;//不暂停
score = 0;
moveCount = 0;//吃到食物前移动的次数为0
isHave = new boolean[X][Y];//初始化小蛇活动的范围
for (int i = 0; i < X; i++) {
isHave[i] = new boolean[Y];
Arrays.fill(isHave[i], false);//填充数据
}
int initLength = X > 20 ? 10 : X / 2;//初始化小蛇,如果横向位置超过20个,长度为10,否则为横向位置的一半
snake.clear();
int x = X / 2;//初始居中显示
int y = Y / 2;
for (int i = 0; i < initLength; i++) {
snake.addLast(new Move(x, y));//添加蛇移动的位置
isHave[x][y] = true;
x++;
}
aliment = createAliment();//创建食物
isHave[aliment.move_X][aliment.move_Y] = true;//在指定位置上有蛇或食物
}
public boolean move() {//蛇运行一步
Move snakeHead = (Move) snake.getFirst();//获得蛇头的位置
int headX = snakeHead.move_X;//获得蛇的横坐标
int headY = snakeHead.move_Y;//获得蛇的纵坐标
switch (move_Direction) {//分支循环判断蛇的运动
case UP://向上移动
headY--;//纵坐标减1
break;//跳出支循环
case DOWN://向下移动
headY++;//纵坐标加1
break;
case LEFT://向左移动
headX--;//横坐标减1
break;
case RIGHT://向上移动
headX++;//横坐标加1
break;
}
if ((0 <= headX && headX < X) && (0 <= headY && headY < Y)) {
if (isHave[headX][headY]) {//如果指定位置有蛇或食物
if (headX == aliment.move_X && headY == aliment.move_Y) {
snake.addFirst(aliment);//蛇头增长表示吃到食物
int scoreGet = (10000 - 200 * moveCount) / timeSpace;//分数与改变方向次数和速度有关
score += scoreGet > 0 ? scoreGet : 10;
moveCount = 0;
aliment = createAliment(); //创建新的食物
isHave[aliment.move_X][aliment.move_Y] = true;//设置食物所在位置
return true;
} else {
return false;//小蛇吃到自己
}
} else {
snake.addFirst(new Move(headX, headY));//添加小蛇的身体
isHave[headX][headY] = true;
snakeHead = (Move) snake.removeLast();//获得蛇头信息
isHave[snakeHead.move_X][snakeHead.move_Y] = false;
moveCount++;
return true;
}
}
return false;//碰到外壁失败
}
public void changeDirection(int dir) {//改变蛇运动的方向
if (move_Direction % 2 != dir % 2) {//改变的方向不与原方向相同或相反
move_Direction = dir;
}
}
private Move createAliment() {// 创建食物
int x = 0;
int y = 0;
do {
Random r = new Random();//随机获取位置
x = r.nextInt(X);
y = r.nextInt(Y);
} while (isHave[x][y]);
return new Move(x, y);//返回食物的新位置
}
public void speedUp() {// 加速运行
timeSpace *= speedChange;
}
public void speedDown() {// 减速运行
timeSpace /= speedChange;
}
public void changePauseState() {// 改变暂停状态
paused = !paused;
}
public boolean isRunning() {//判断是否运动
return running;
}
public void setRunning(boolean running) {//设置运动标识
this.running = running;
}
public LinkedList getMoveList() {//获得链表数据(蛇的身体)
return snake;
}
public Move getAliment() {//获得食物
return aliment;
}
public int getScore() {//获得得分
return score;
}
}
class SnakeFrame extends JFrame implements Observer {//贪吃蛇的视图,MVC中的View
public static final int gridWidth = 10;//格子的宽度
public static final int gridHeight = 10;//格子的高度
private int gameWidth;//画面的宽度
private int gameHeight;//画面的高度
private int gameX = 0;//画面左上角横位置
private int startY = 0;//画面左上角纵坐标
JLabel score; //声明分数标签
Canvas canvas; //声明画布
public SnakeFrame() {//// 默认构造方法调用带参数的构造方法
this(30, 40, 0, 0);
}
public SnakeFrame(int X, int Y) {// 带参数的构造方法进行初始化
this(X, Y, 0, 0);
}
public SnakeFrame(int X, int Y, int startX, int startY) {// 带参数的构造方法进行初始化
this.gameWidth = X * gridWidth;
this.gameHeight = Y * gridHeight;
this.gameX = startX;
this.startY = startY;
init();
}
private void init() {// 初始化游戏界面
this.setTitle("贪吃的小蛇");//设置界面的标题
this.setLocation(gameX, startY);//设置界面的位置(方位)
Container cp = this.getContentPane();//获得一容器
score = new JLabel("成绩:");
cp.add(score, BorderLayout.SOUTH);//将成绩放在界面的南侧
canvas = new Canvas();//创建中间的游戏显示区域
canvas.setSize(gameWidth + 1, gameHeight + 1);//设置区域的大小
cp.add(canvas, BorderLayout.CENTER);//区域放在界面的中间
this.pack();//根据组件的最优尺寸来进行布局
this.setResizable(false);//窗体不能最小化
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置默认关闭操作
this.setVisible(true);//可视
}
public void update(Observable observer, Object obj) {// 实现Observer接口定义的update方法
MoveOperate operate = (MoveOperate)observer; //获取被监控的模型
Graphics graphics = canvas.getGraphics();// 获得画笔
graphics.setColor(Color.WHITE);//设置颜色为白色
graphics.fillRect(0, 0, gameWidth, gameHeight);//填充一个矩形
graphics.setColor(Color.BLACK);//重新设置颜色
LinkedList list = operate.getMoveList();//获得蛇体
Iterator it = list.iterator();
while (it.hasNext()) {
Move move = (Move) it.next();
drawMove(graphics, move);//调用方法画方格
}
graphics.setColor(Color.RED);//设置颜色表示食物
Move move = operate.getAliment();//获得食物
drawMove(graphics, move);
this.updateScore(operate.getScore());//更新成绩
}
private void drawMove(Graphics g, Move n) {//根据移动路径画格子
g.fillRect(n.move_X * gridWidth, n.move_Y * gridHeight, gridWidth - 1,
gridHeight - 1);//填充一个矩形
}
public void updateScore(int score) {//更新成绩
String s = "成绩: " + score;
this.score.setText(s);//设置成绩标签的值
}
}
class ControlSnake implements KeyListener {//控制蛇的运动
private MoveOperate snake; //贪吃的小蛇模型
private SnakeFrame frame; // 蛇的对象
private int X;//蛇移动区域横坐标
private int Y;//蛇移动区域纵坐标
public ControlSnake() {// 默认构造方法进行初始化
this.X = 30;
this.Y = 40;
}
public ControlSnake(int X, int Y) {// 带参数的构造方法进行初始化
this();
if ((10 < X) && (X < 200) && (10 < Y) && (Y < 200)) {//判断移动区域
this.X = X;
this.Y = Y;
} else {
System.out.println("初始化参数出错!");
}
initSnake();//调用初始化方法
}
private void initSnake() {// 初始化
this.snake = new MoveOperate(X, Y); //创建蛇模型
this.frame = new SnakeFrame(X, Y, 500, 200);
this.snake.addObserver(this.frame); //为模型添加对象
this.frame.addKeyListener(this); // 添加键盘事件
(new Thread(this.snake)).start();//启动线程蛇运动
}
public void keyPressed(KeyEvent e) {// 键盘按下事件
int keyCode = e.getKeyCode();
if (snake.isRunning()) {// 只有在贪吃蛇处于运行状态下,才处理的按键事件
switch (keyCode) {
case KeyEvent.VK_ADD://按下数字键盘上的+
case KeyEvent.VK_PAGE_UP://按下PageUp键
snake.speedUp();
break;
case KeyEvent.VK_SUBTRACT://按下数字键盘上的-
case KeyEvent.VK_PAGE_DOWN://按下PageDown
snake.speedDown();
break;
case KeyEvent.VK_SPACE://按下空格键
case KeyEvent.VK_P://按下PauseBreak
snake.changePauseState();
break;
case KeyEvent.VK_UP://按下向上键
snake.changeDirection(MoveOperate.UP);
break;
case KeyEvent.VK_DOWN://按下向下键
snake.changeDirection(MoveOperate.DOWN);
break;
case KeyEvent.VK_LEFT://按下向左键
snake.changeDirection(MoveOperate.LEFT);
break;
case KeyEvent.VK_RIGHT://按下向右键
snake.changeDirection(MoveOperate.RIGHT);
break;
default:
}
}
if (keyCode == KeyEvent.VK_F || keyCode == KeyEvent.VK_J
|| keyCode == KeyEvent.VK_ENTER) {//按下回车、F、J键开始游戏
snake.resetGame();
}
if (keyCode == KeyEvent.VK_S) {//按下S键停止游戏
snake.setRunning(false);
}
}
public void keyReleased(KeyEvent e) {//键盘弹起事件
}
public void keyTyped(KeyEvent e) {//有字符被输入事件
}
}
public class SmallSnake {//操作实现小蛇吃食物的操作
public static void main(String[] args) {//java程序主入口处
new ControlSnake(40,30);//实例化对象
}
}
贪吃蛇
最新推荐文章于 2025-07-21 23:35:20 发布