Java课程设计:贪吃蛇游戏
项目概述
本项目旨在使用Java语言和Swing图形用户界面库,开发一个功能完整、界面美观、可交互的贪吃蛇游戏,游戏将实现贪吃蛇的基本功能,包括:
- 蛇的移动、转向。
- 随机生成食物。
- 蛇吃到食物后身体变长,并加分。
- 检测蛇是否撞到墙壁或自身,如果碰撞则游戏结束。
- 实时显示当前分数和最高分。
- 游戏的开始、暂停和重新开始功能。
开发环境与技术选型
- 开发语言: Java SE
- 开发工具: IntelliJ IDEA / Eclipse / VS Code
- GUI库: Java Swing (因其轻量、易用,是Java课程设计的首选)
- 核心思想: 面向对象编程,将游戏中的实体(如蛇、食物、游戏面板)抽象为独立的类。
核心设计思路
我们将采用面向对象的思想,将游戏的各个部分拆分成不同的类,使代码结构清晰、易于维护和扩展。

GamePanel类 (游戏面板): 这是游戏的核心,负责绘制所有的游戏元素(蛇、食物、分数等),并处理游戏的主循环逻辑。Snake类 (蛇): 代表游戏中的蛇,它应该包含蛇的身体(用一个List或数组存储每个身体节点的坐标)、移动方向、增长逻辑和判断自身是否碰撞的逻辑。Food类 (食物): 代表游戏中的食物,它只需要记录自己的位置坐标。GameController类 (游戏控制器/主窗口): 创建并管理GamePanel,设置游戏窗口,并处理游戏状态(如开始、暂停)。
详细实现步骤
第一步:创建项目与主窗口
创建一个Java项目,并创建一个主类 SnakeGame。
// GameController.java
import javax.swing.*;
import java.awt.*;
public class SnakeGame extends JFrame {
public SnakeGame() {
// 1. 创建游戏面板实例
GamePanel gamePanel = new GamePanel();
// 2. 将面板添加到窗口中
this.add(gamePanel);
// 3. 设置窗口基本属性
this.setTitle("Java 贪吃蛇");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 关闭窗口时退出程序
this.setResizable(false); // 禁止调整窗口大小
this.pack(); // 自动调整窗口大小以适应组件大小
this.setLocationRelativeTo(null); // 窗口居中显示
}
public static void main(String[] args) {
// 在事件调度线程中创建和显示GUI,这是Swing的最佳实践
SwingUtilities.invokeLater(() -> {
SnakeGame game = new SnakeGame();
game.setVisible(true);
});
}
}
第二步:实现 GamePanel 类
这是最核心的类,包含游戏的所有逻辑。
// GamePanel.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import java.util.ArrayList;
import java.util.List;
public class GamePanel extends JPanel implements ActionListener {
// 定义游戏区域尺寸
private final int WIDTH = 600;
private final int HEIGHT = 600;
private final int UNIT_SIZE = 25; // 每个格子的大小
private final int GAME_UNITS = (WIDTH * HEIGHT) / (UNIT_SIZE * UNIT_SIZE); // 游戏区域总格子数
// 定义游戏状态
private boolean running = false;
private Timer timer;
private int delay = 100; // 游戏速度(毫秒)
// 蛇的身体
private final List<Integer> snakeX = new ArrayList<>();
private final List<Integer> snakeY = new ArrayList<>();
// 初始移动方向
private char direction = 'R'; // R: Right, L: Left, U: Up, D: Down
// 食物坐标
private int foodX;
private int foodY;
// 分数
private int score = 0;
private int highScore = 0;
public GamePanel() {
this.setPreferredSize(new Dimension(WIDTH, HEIGHT));
this.setBackground(Color.BLACK);
this.setFocusable(true); // 使面板可以获取键盘焦点
this.addKeyListener(new MyKeyAdapter());
startGame();
}
public void startGame() {
// 初始化蛇
snakeX.clear();
snakeY.clear();
snakeX.add(3 * UNIT_SIZE);
snakeY.add(3 * UNIT_SIZE);
direction = 'R';
score = 0;
// 生成第一个食物
newFood();
running = true;
timer = new Timer(delay, this);
timer.start();
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
public void draw(Graphics g) {
if (running) {
// 绘制食物
g.setColor(Color.RED);
g.fillRect(foodX, foodY, UNIT_SIZE, UNIT_SIZE);
// 绘制蛇
for (int i = 0; i < snakeX.size(); i++) {
if (i == 0) { // 蛇头
g.setColor(Color.GREEN);
} else { // 蛇身
g.setColor(new Color(45, 180, 0));
}
g.fillRect(snakeX.get(i), snakeY.get(i), UNIT_SIZE, UNIT_SIZE);
}
// 绘制分数
g.setColor(Color.WHITE);
g.setFont(new Font("Ink Free", Font.BOLD, 40));
FontMetrics metrics = getFontMetrics(g.getFont());
g.drawString("Score: " + score, (WIDTH - metrics.stringWidth("Score: " + score)) / 2, g.getFont().getSize());
} else {
gameOver(g);
}
}
public void newFood() {
Random random = new Random();
foodX = random.nextInt((int)(WIDTH / UNIT_SIZE)) * UNIT_SIZE;
foodY = random.nextInt((int)(HEIGHT / UNIT_SIZE)) * UNIT_SIZE;
}
public void move() {
// 移动蛇身:除了头,每个身体节点移动到前一个节点的位置
for (int i = snakeX.size() - 1; i > 0; i--) {
snakeX.set(i, snakeX.get(i - 1));
snakeY.set(i, snakeY.get(i - 1));
}
// 根据方向移动蛇头
switch (direction) {
case 'U':
snakeY.set(0, snakeY.get(0) - UNIT_SIZE);
break;
case 'D':
snakeY.set(0, snakeY.get(0) + UNIT_SIZE);
break;
case 'L':
snakeX.set(0, snakeX.get(0) - UNIT_SIZE);
break;
case 'R':
snakeX.set(0, snakeX.get(0) + UNIT_SIZE);
break;
}
}
public void checkFood() {
// 如果蛇头和食物坐标重合,表示吃到食物
if (snakeX.get(0) == foodX && snakeY.get(0) == foodY) {
score++;
// 蛇身增长:在尾部添加一个节点(位置和尾部一样,移动时自然会跟上来)
snakeX.add(snakeX.get(snakeX.size() - 1));
snakeY.add(snakeY.get(snakeY.size() - 1));
newFood();
}
}
public void checkCollisions() {
// 检查是否撞到自己
for (int i = snakeX.size() - 1; i > 0; i--) {
if (snakeX.get(0) == snakeX.get(i) && snakeY.get(0) == snakeY.get(i)) {
running = false;
}
}
// 检查是否撞墙
if (snakeX.get(0) < 0 || snakeX.get(0) >= WIDTH || snakeY.get(0) < 0 || snakeY.get(0) >= HEIGHT) {
running = false;
}
if (!running) {
timer.stop();
if (score > highScore) {
highScore = score;
}
}
}
public void gameOver(Graphics g) {
// 游戏结束文字
g.setColor(Color.RED);
g.setFont(new Font("Ink Free", Font.BOLD, 75));
FontMetrics metrics1 = getFontMetrics(g.getFont());
g.drawString("Game Over", (WIDTH - metrics1.stringWidth("Game Over")) / 2, HEIGHT / 2);
// 分数
g.setColor(Color.WHITE);
g.setFont(new Font("Ink Free", Font.BOLD, 40));
FontMetrics metrics2 = getFontMetrics(g.getFont());
g.drawString("Score: " + score, (WIDTH - metrics2.stringWidth("Score: " + score)) / 2, HEIGHT / 2 + 50);
// 最高分
g.drawString("High Score: " + highScore, (WIDTH - metrics2.stringWidth("High Score: " + highScore)) / 2, HEIGHT / 2 + 100);
// 重新开始提示
g.setFont(new Font("Ink Free", Font.BOLD, 20));
g.drawString("Press SPACE to restart", (WIDTH - metrics2.stringWidth("Press SPACE to restart")) / 2, HEIGHT / 2 + 150);
}
@Override
public void actionPerformed(ActionEvent e) {
if (running) {
move();
checkFood();
checkCollisions();
}
repaint(); // 重新绘制面板
}
// 内部类,用于处理键盘事件
private class MyKeyAdapter extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
if (running) {
// 控制转向,防止180度掉头
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;
}
} else {
// 游戏结束时,按空格键重新开始
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
startGame();
}
}
}
}
}
第三步:测试与运行
将上述两个类文件放在同一个包下,然后运行 SnakeGame.java 的 main 方法,你应该能看到一个黑色的窗口,里面有绿色的蛇和红色的食物,并且可以通过方向键控制蛇的移动。
功能扩展建议 (优秀加分项)
如果你的课程设计要求更高,可以考虑增加以下功能:

- 难度选择: 在游戏开始前,提供“简单”、“中等”、“困难”等选项,通过调整
Timer的delay值来改变游戏速度。 - 障碍物: 在游戏地图中随机或固定位置生成一些障碍物,蛇撞到障碍物也会游戏结束。
- 不同类型的食物:
- 特殊食物: 吃到后不仅能加分,还能获得特殊效果,如减速、减速一段时间、蛇身变短等。
- 负分食物: 生成一种特殊的食物,吃到后会扣分。
- 游戏暂停功能: 添加一个“暂停”按钮或按
P键暂停游戏。 - 音效: 为吃食物、游戏结束等事件添加简单的音效。
- 更精美的UI: 使用图片来代替简单的色块绘制蛇和食物,增加游戏的美感。
项目总结
本次课程设计成功实现了一个基于Java Swing的贪吃蛇游戏,通过该项目,我们:
- 掌握了Java Swing的基本用法,如
JFrame,JPanel,Timer等组件。 - 深入理解了面向对象编程思想,将游戏逻辑合理地封装在不同的类中。
- 实践了事件处理机制,通过
KeyListener实现了键盘交互。 - 熟悉了游戏开发的基本流程,包括游戏循环、状态管理、碰撞检测等核心逻辑。
这个项目结构清晰,代码可读性强,并且具有良好的扩展性,非常适合作为Java课程的课程设计。
祝你课程设计顺利!

版权声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。