java课程设计贪吃蛇

99ANYc3cd6 课程介绍 1

Java课程设计:贪吃蛇游戏

项目概述

本项目旨在使用Java语言和Swing图形用户界面库,开发一个功能完整、界面美观、可交互的贪吃蛇游戏,游戏将实现贪吃蛇的基本功能,包括:

  • 蛇的移动、转向。
  • 随机生成食物。
  • 蛇吃到食物后身体变长,并加分。
  • 检测蛇是否撞到墙壁或自身,如果碰撞则游戏结束。
  • 实时显示当前分数和最高分。
  • 游戏的开始、暂停和重新开始功能。

开发环境与技术选型

  • 开发语言: Java SE
  • 开发工具: IntelliJ IDEA / Eclipse / VS Code
  • GUI库: Java Swing (因其轻量、易用,是Java课程设计的首选)
  • 核心思想: 面向对象编程,将游戏中的实体(如蛇、食物、游戏面板)抽象为独立的类。

核心设计思路

我们将采用面向对象的思想,将游戏的各个部分拆分成不同的类,使代码结构清晰、易于维护和扩展。

java课程设计贪吃蛇-第1张图片-指南针培训网
  1. GamePanel 类 (游戏面板): 这是游戏的核心,负责绘制所有的游戏元素(蛇、食物、分数等),并处理游戏的主循环逻辑。
  2. Snake 类 (蛇): 代表游戏中的蛇,它应该包含蛇的身体(用一个List或数组存储每个身体节点的坐标)、移动方向、增长逻辑和判断自身是否碰撞的逻辑。
  3. Food 类 (食物): 代表游戏中的食物,它只需要记录自己的位置坐标。
  4. 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.javamain 方法,你应该能看到一个黑色的窗口,里面有绿色的蛇和红色的食物,并且可以通过方向键控制蛇的移动。

功能扩展建议 (优秀加分项)

如果你的课程设计要求更高,可以考虑增加以下功能:

java课程设计贪吃蛇-第2张图片-指南针培训网
  1. 难度选择: 在游戏开始前,提供“简单”、“中等”、“困难”等选项,通过调整Timerdelay值来改变游戏速度。
  2. 障碍物: 在游戏地图中随机或固定位置生成一些障碍物,蛇撞到障碍物也会游戏结束。
  3. 不同类型的食物:
    • 特殊食物: 吃到后不仅能加分,还能获得特殊效果,如减速、减速一段时间、蛇身变短等。
    • 负分食物: 生成一种特殊的食物,吃到后会扣分。
  4. 游戏暂停功能: 添加一个“暂停”按钮或按P键暂停游戏。
  5. 音效: 为吃食物、游戏结束等事件添加简单的音效。
  6. 更精美的UI: 使用图片来代替简单的色块绘制蛇和食物,增加游戏的美感。

项目总结

本次课程设计成功实现了一个基于Java Swing的贪吃蛇游戏,通过该项目,我们:

  • 掌握了Java Swing的基本用法,如JFrame, JPanel, Timer等组件。
  • 深入理解了面向对象编程思想,将游戏逻辑合理地封装在不同的类中。
  • 实践了事件处理机制,通过KeyListener实现了键盘交互。
  • 熟悉了游戏开发的基本流程,包括游戏循环、状态管理、碰撞检测等核心逻辑。

这个项目结构清晰,代码可读性强,并且具有良好的扩展性,非常适合作为Java课程的课程设计。

祝你课程设计顺利!

java课程设计贪吃蛇-第3张图片-指南针培训网

标签: 贪吃蛇算法 Java GUI 键盘事件监听

抱歉,评论功能暂时关闭!