贪吃蛇小游戏
**
1. 项目概述
本贪吃蛇游戏是基于Java Swing框架开发的经典游戏实现,具有完整的游戏流程和用户交互功能。游戏采用面向对象的设计思想,实现了蛇的移动、食物生成、碰撞检测等核心机制,并提供了美观的图形界面和流畅的游戏体验。
2. 系统架构
2.1 类结构设计

mermaid
classDiagram
class SnakeGame {
-TILE_SIZE: int
-WIDTH: int
-HEIGHT: int
-ALL_TILES: int
-DELAY_SLOW: int
-DELAY_MEDIUM: int
-DELAY_FAST: int
-x: int[]
-y: int[]
-bodyParts: int
-applesEaten: int
-appleX: int
-appleY: int
-currentDelay: int
-direction: char
-running: boolean
-inStartMenu: boolean
-timer: Timer
-random: Random
+SnakeGame()
+startGame()
+newApple()
+moveSnake()
+checkApple()
+checkCollisions()
-gameOver()
}
class GamePanel {
-selectedSpeed: int
+GamePanel()
+paintComponent(Graphics)
-drawStartMenu(Graphics)
-drawGame(Graphics)
-drawGameOver(Graphics)
}
class GameLoop {
+actionPerformed(ActionEvent)
}
SnakeGame --> GamePanel
SnakeGame --> GameLoop
GamePanel --|> JPanel
GameLoop ..|> ActionListener
2.2 关键数据结构
坐标数组:
x[]和y[]:存储蛇身体各部分的坐标
长度固定为ALL_TILES(WIDTH*HEIGHT)
实际使用长度由bodyParts控制
游戏状态变量:
running:标识游戏是否进行中
inStartMenu:标识是否在开始菜单
direction:当前移动方向(‘U’,‘D’,‘L’,‘R’)
3. 核心功能实现
3.1 游戏初始化
public SnakeGame() {
random = new Random();
this.setTitle("贪吃蛇游戏");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
// 初始化蛇的位置(居中)
for (int i = 0; i < bodyParts; i++) {
x[i] = (WIDTH/2 - i) * TILE_SIZE;
y[i] = (HEIGHT/2) * TILE_SIZE;
}
GamePanel gamePanel = new GamePanel();
this.add(gamePanel);
this.pack();
this.setSize(WIDTH * TILE_SIZE, HEIGHT * TILE_SIZE + 30);
this.setLocationRelativeTo(null);
this.setVisible(true);
}
创建随机数生成器
设置窗口基本属性
初始化蛇的位置(居中水平排列)
创建并添加游戏面板
设置窗口大小和位置
3.2 游戏主循环
private class GameLoop implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if (running) {
moveSnake();
checkApple();
checkCollisions();
repaint();
}
}
}
移动蛇:更新蛇身坐标
检查苹果:判断是否吃到食物
碰撞检测:检查是否撞墙或自身
重绘界面:更新游戏画面
3.3 蛇移动算法
public void moveSnake() {
// 从尾部开始更新位置
for (int i = bodyParts; i > 0; i--) {
x[i] = x[i - 1];
y[i] = y[i - 1];
}
// 根据方向移动头部
switch (direction) {
case 'U': y[0] -= TILE_SIZE; break;
case 'D': y[0] += TILE_SIZE; break;
case 'L': x[0] -= TILE_SIZE; break;
case 'R': x[0] += TILE_SIZE; break;
}
}
采用数组移位方式实现蛇身移动
从尾部开始,每个部分移动到前一个部分的位置
头部根据当前方向移动一个格子
3.4 食物生成
public void newApple() {
boolean onSnake;
do {
onSnake = false;
appleX = random.nextInt(WIDTH) * TILE_SIZE;
appleY = random.nextInt(HEIGHT) * TILE_SIZE;
for (int i = 0; i < bodyParts; i++) {
if (appleX == x[i] && appleY == y[i]) {
onSnake = true;
break;
}
}
} while (onSnake);
}
使用do-while循环确保食物不会生成在蛇身上
随机生成坐标并检查是否与蛇身重叠
直到找到有效位置才结束循环
3.5 碰撞检测
public void checkCollisions() {
// 检查撞到自己
for (int i = bodyParts; i > 0; i--) {
if (x[0] == x[i] && y[0] == y[i]) {
gameOver();
return;
}
}
// 检查撞墙
if (x[0] < 0 || x[0] >= WIDTH * TILE_SIZE ||
y[0] < 0 || y[0] >= HEIGHT * TILE_SIZE) {
gameOver();
}
}
自身碰撞:检查头部是否与任何身体部分重合
墙壁碰撞:检查头部是否超出游戏边界
任一条件满足即触发游戏结束
4. 用户交互设计
4.1 输入处理
this.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (inStartMenu) {
// 开始菜单按键处理
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
selectedSpeed = (selectedSpeed - 1 + 3) % 3;
repaint();
break;
case KeyEvent.VK_DOWN:
selectedSpeed = (selectedSpeed + 1) % 3;
repaint();
break;
case KeyEvent.VK_SPACE:
// 根据选择设置速度
switch (selectedSpeed) {
case 0: currentDelay = DELAY_SLOW; break;
case 1: currentDelay = DELAY_MEDIUM; break;
case 2: currentDelay = DELAY_FAST; break;
}
startGame();
break;
}
} else if (!running && e.getKeyCode() == KeyEvent.VK_SPACE) {
// 重置游戏
for (int i = 0; i < bodyParts; i++) {
x[i] = (WIDTH/2 - i) * TILE_SIZE;
y[i] = (HEIGHT/2) * TILE_SIZE;
}
bodyParts = 3;
applesEaten = 0;
direction = 'R';
inStartMenu = true;
repaint();
} else if (running) {
// 游戏中的按键处理
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
if (direction != 'R') direction = 'L';
break;
case KeyEvent.VK_RIGHT:
if (direction != 'L') direction = 'R';
break;
case KeyEvent.VK_UP:
if (direction != 'D') direction = 'U';
break;
case KeyEvent.VK_DOWN:
if (direction != 'U') direction = 'D';
break;
}
}
}
});
开始菜单:上下键选择速度,空格键确认
游戏中:方向键控制移动方向
游戏结束:空格键返回主菜单
防止180度转向(不能直接反向移动)
4.2 界面绘制
开始菜单
private void drawStartMenu(Graphics g) {
// 半透明背景
g.setColor(new Color(0, 0, 0, 200));
g.fillRect(0, 0, getWidth(), getHeight());
// 标题
g.setColor(Color.GREEN);
Font titleFont = new Font("微软雅黑", Font.BOLD, 50);
g.setFont(titleFont);
FontMetrics metrics = g.getFontMetrics();
String title = "贪吃蛇游戏";
g.drawString(title, (getWidth() - metrics.stringWidth(title)) / 2, getHeight() / 3);
// 速度选择
Font speedFont = new Font("微软雅黑", Font.BOLD, 30);
g.setFont(speedFont);
metrics = g.getFontMetrics();
String[] speedOptions = {
"慢速", "中速", "快速"};
int startY = getHeight() / 2;
for (int i = 0; i < speedOptions.length; i++) {
if (i == selectedSpeed) {
g.setColor(Color.YELLOW);
g.fillRect(getWidth() / 2 - 100, startY + i * 40 - 25, 200, 35);
g.setColor(Color.BLACK);
} else {
g.setColor(Color.WHITE);
}
g.drawString(speedOptions[i], (getWidth() - metrics.stringWidth(speedOptions[i])) / 2, startY + i * 40);
}
// 提示
Font hintFont = new Font("微软雅黑", Font.PLAIN, 20);
g.setFont(hintFont);
metrics = g.getFontMetrics();
g.setColor(Color.WHITE);
String hint = "按空格键开始游戏";
int hintX = (getWidth() - metrics.stringWidth(hint)) / 2;
int hintY = getHeight() * 3 / 4;
g.drawString(hint, hintX, hintY);
}
游戏界面
private void drawGame(Graphics g) {
// 绘制游戏元素
g.setColor(Color.RED);
g.fillOval(appleX, appleY, TILE_SIZE, TILE_SIZE);
for (int i = 0; i < bodyParts; i++) {
if (i == 0) {
g.setColor(Color.GREEN);
} else {
g.setColor(new Color(45, 180, 0));
}
g.fillRect(x[i], y[i], TILE_SIZE, TILE_SIZE);
}
g.setColor(Color.WHITE);
Font font = new Font("微软雅黑", Font.BOLD, 20);
g.setFont(font);
g.drawString("得分: " + applesEaten, 10, 20);
}
游戏结束界面
private void drawGameOver(Graphics g) {
// 半透明背景
g.setColor(new Color(0, 0, 0, 150));
g.fillRect(0, 0, getWidth(), getHeight());
// 设置字体
Font font = new Font("微软雅黑", Font.BOLD, 50);
g.setFont(font);
FontMetrics metrics = g.getFontMetrics();
// Game Over文字
g.setColor(Color.RED);
String gameOverText = "游戏结束";
g.drawString(gameOverText, (getWidth() - metrics.stringWidth(gameOverText)) / 2, getHeight() / 2 - 40);
// 得分显示
g.setColor(Color.WHITE);
font = new Font("微软雅黑", Font.BOLD, 30);
g.setFont(font);
metrics = g.getFontMetrics();
String scoreText = "得分: " + applesEaten;
g.drawString(scoreText, (getWidth() - metrics.stringWidth(scoreText)) / 2, getHeight() / 2 + 20);
// 重新开始提示
font = new Font("微软雅黑", Font.PLAIN, 20);
g.setFont(font);
metrics = g.getFontMetrics();
String restartText = "按空格键返回主菜单";
g.drawString(restartText, (getWidth() - metrics.stringWidth(restartText)) / 2, getHeight() / 2 + 70);
}
5. 总结
本贪吃蛇游戏实现完整,功能完善,代码结构清晰。通过合理的类设计和状态管理,实现了流畅的游戏体验。游戏界面美观,操作简单,是学习Java Swing和游戏开发的优秀示例。后续可通过数据结构优化和功能扩展进一步提升游戏性能和可玩性。






















暂无评论内容