大三Java课设:一行行敲出来的贪吃蛇,老师以为我是CV的

文章目录

大三Java课设,我之前写过python贪吃蛇,那篇文章比较火所以写了个JAVA贪吃蛇

班上40个人,30个交了贪吃蛇。老师在讲台上翻作业,翻到第10个脸就绿了------"你们这是交作业还是交Ctrl+C?"

我交的也是贪吃蛇,但是自己一行一行敲的。老师后来把我叫过去,问能不能把代码分享给班上参考。

码龄三年了,今天把三个版本的贪吃蛇全部开源:从Swing数组版,到带计分暂停的JavaFX版,再到带AI自动寻路的炫酷版。你拿去交课设也好,面试当项目也好,都够用了。

源码我放三个地方,哪个方便用哪个:

版本 GitHub Gitee 网盘
初级版 GitHub Gitee 百度网盘 提取码: ngg7
中级版 GitHub Gitee -
高级版 GitHub Gitee -

📸 如何获得效果图: 把代码跑起来,用系统截图工具(Win+Shift+S或Mac Command+Shift+4)截几张运行界面,替换下面标注的位置即可。

图片素材:链接:https://pan.baidu.com/s/1MKhUOA4WeYFFOsBoblh0kQ?pwd=xmrr#list/path=百度网盘网页链接 提取码:xmrr

一、先搭个能跑的窗口

不管哪个版本,第一步都是搭一个能跑起来的窗口。Swing的JFrame最简单:

java 复制代码
import javax.swing.*;

public class SnakeGame extends JFrame {
    public SnakeGame() {
        setTitle("贪吃蛇");
        setBounds(10, 10, 900, 720);
        setResizable(false);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        add(new GamePanel());
        setVisible(true);
    }

    public static void main(String[] args) {
        new SnakeGame();
    }
}

900×720的窗口,25×25的格子,刚好画28×24的网格。

📸

二、初级版:Swing数组实现基础贪吃蛇

这个版本约200行代码,实现功能:绘制窗口、控制移动、吃食物变长、撞墙/撞自身游戏结束。

2.1 游戏面板 GamePanel.java

java 复制代码
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;

public class GamePanel extends JPanel implements KeyListener, ActionListener {
    int length = 3;
    int[] snakeX = new int[600];
    int[] snakeY = new int[500];
    String direction = "R";
    
    int foodX, foodY;
    Random random = new Random();
    int score = 0;
    
    boolean isStarted = false;
    boolean isFailed = false;
    
    Timer timer = new Timer(100, this);
    
    public GamePanel() {
        init();
        this.setFocusable(true);
        this.addKeyListener(this);
        timer.start();
    }
    
    private void init() {
        length = 3;
        snakeX[0] = 100; snakeY[0] = 100;
        snakeX[1] = 75;  snakeY[1] = 100;
        snakeX[2] = 50;  snakeY[2] = 100;
        direction = "R";
        
        foodX = 25 + 25 * random.nextInt(34);
        foodY = 75 + 25 * random.nextInt(24);
        score = 0;
    }
    
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        this.setBackground(Color.WHITE);
        
        // 头部区域
        g.setColor(Color.LIGHT_GRAY);
        g.fillRect(25, 11, 850, 50);
        
        // 游戏区域
        g.setColor(Color.BLACK);
        g.fillRect(25, 75, 850, 600);
        
        // 蛇头
        g.setColor(Color.RED);
        g.fillRect(snakeX[0], snakeY[0], 25, 25);
        
        // 蛇身
        g.setColor(Color.GREEN);
        for (int i = 1; i < length; i++) {
            g.fillRect(snakeX[i], snakeY[i], 25, 25);
        }
        
        // 食物
        g.setColor(Color.ORANGE);
        g.fillOval(foodX, foodY, 25, 25);
        
        // 分数
        g.setColor(Color.WHITE);
        g.setFont(new Font("微软雅黑", Font.BOLD, 18));
        g.drawString("Score: " + score, 700, 45);
        
        if (!isStarted) {
            g.setColor(Color.WHITE);
            g.setFont(new Font("微软雅黑", Font.BOLD, 30));
            g.drawString("Press Space to Start", 300, 350);
        }
        
        if (isFailed) {
            g.setColor(Color.RED);
            g.setFont(new Font("微软雅黑", Font.BOLD, 30));
            g.drawString("GAME OVER! Press Space", 250, 350);
        }
    }
    
    @Override
    public void actionPerformed(ActionEvent e) {
        if (isStarted && !isFailed) {
            // 身体前移
            for (int i = length - 1; i > 0; i--) {
                snakeX[i] = snakeX[i - 1];
                snakeY[i] = snakeY[i - 1];
            }
            
            // 移动蛇头
            switch (direction) {
                case "R": snakeX[0] += 25; break;
                case "L": snakeX[0] -= 25; break;
                case "U": snakeY[0] -= 25; break;
                case "D": snakeY[0] += 25; break;
            }
            
            // 边界检测
            if (snakeX[0] > 850 || snakeX[0] < 25 || snakeY[0] > 650 || snakeY[0] < 75) {
                isFailed = true;
            }
            
            // 自身碰撞
            for (int i = 1; i < length; i++) {
                if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
                    isFailed = true;
                    break;
                }
            }
            
            // 吃到食物
            if (snakeX[0] == foodX && snakeY[0] == foodY) {
                length++;
                score += 10;
                foodX = 25 + 25 * random.nextInt(34);
                foodY = 75 + 25 * random.nextInt(24);
            }
            
            repaint();
        }
        timer.start();
    }
    
    @Override
    public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        
        if (keyCode == KeyEvent.VK_SPACE) {
            if (isFailed) {
                isFailed = false;
                init();
            } else {
                isStarted = !isStarted;
            }
            repaint();
        }
        
        // 禁止掉头
        if (keyCode == KeyEvent.VK_UP && !direction.equals("D")) {
            direction = "U";
        } else if (keyCode == KeyEvent.VK_DOWN && !direction.equals("U")) {
            direction = "D";
        } else if (keyCode == KeyEvent.VK_LEFT && !direction.equals("R")) {
            direction = "L";
        } else if (keyCode == KeyEvent.VK_RIGHT && !direction.equals("L")) {
            direction = "R";
        }
    }
    
    @Override public void keyTyped(KeyEvent e) {}
    @Override public void keyReleased(KeyEvent e) {}
}

在这里

三、中级版:JavaFX实现带UI交互

这个版本约500行代码,用JavaFX替代Swing,界面更现代,增加了开始/暂停/继续、生命值系统和最高分记录。

3.1 主入口 Main.java

java 复制代码
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("/game.fxml"));
        primaryStage.setTitle("贪吃蛇 - JavaFX中级版");
        primaryStage.setScene(new Scene(root, 800, 600));
        primaryStage.setResizable(false);
        primaryStage.show();
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}

3.2 蛇实体类 Snake.java

java 复制代码
import java.util.LinkedList;
import javafx.scene.paint.Color;

public class Snake {
    private LinkedList<Point> body;
    private Direction direction;
    private Color headColor = Color.RED;
    private Color bodyColor = Color.GREEN;
    
    public Snake(int startX, int startY) {
        body = new LinkedList<>();
        body.add(new Point(startX, startY));
        body.add(new Point(startX - 1, startY));
        body.add(new Point(startX - 2, startY));
        direction = Direction.RIGHT;
    }
    
    public void move(boolean grow) {
        Point head = body.getFirst();
        Point newHead = null;
        
        switch (direction) {
            case UP:    newHead = new Point(head.x, head.y - 1); break;
            case DOWN:  newHead = new Point(head.x, head.y + 1); break;
            case LEFT:  newHead = new Point(head.x - 1, head.y); break;
            case RIGHT: newHead = new Point(head.x + 1, head.y); break;
        }
        
        body.addFirst(newHead);
        if (!grow) {
            body.removeLast();
        }
    }
    
    public boolean checkSelfCollision() {
        Point head = body.getFirst();
        for (int i = 1; i < body.size(); i++) {
            if (head.equals(body.get(i))) {
                return true;
            }
        }
        return false;
    }
    
    public Point getHead() { return body.getFirst(); }
    public LinkedList<Point> getBody() { return body; }
    public void setDirection(Direction d) { this.direction = d; }
    public Direction getDirection() { return direction; }
}

3.3 游戏控制器核心片段

java 复制代码
import javafx.animation.AnimationTimer;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;

public class GameController {
    private Canvas canvas;
    private GraphicsContext gc;
    private Snake snake;
    private Food food;
    private int score = 0;
    private int lives = 3;
    private boolean running = false;
    
    private AnimationTimer gameLoop = new AnimationTimer() {
        private long lastUpdate = 0;
        
        @Override
        public void handle(long now) {
            if (!running) return;
            
            if (now - lastUpdate >= 150_000_000) {
                update();
                render();
                lastUpdate = now;
            }
        }
    };
    
    private void update() {
        boolean ateFood = snake.getHead().equals(food.getPosition());
        snake.move(ateFood);
        
        if (ateFood) {
            score += 10;
            food.regenerate(snake.getBody());
        }
        
        if (snake.checkWallCollision(GRID_WIDTH, GRID_HEIGHT) || snake.checkSelfCollision()) {
            lives--;
            if (lives <= 0) {
                gameOver();
            } else {
                resetSnake();
            }
        }
    }
}

📸 截游戏画面放在这里。

四、高级版:带AI、皮肤、排行榜

这个版本约1000+行代码,展示Java游戏开发的完整技术栈。

4.1 AI寻路算法

java 复制代码
import java.awt.Point;
import java.util.*;

public class AIPathFinder {
    private static final int[][] DIRECTIONS = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
    
    public static Direction findNextDirection(Point head, Point food, 
                                               Set<Point> obstacles, 
                                               int gridWidth, int gridHeight) {
        Queue<Point> queue = new LinkedList<>();
        Map<Point, Point> cameFrom = new HashMap<>();
        Set<Point> visited = new HashSet<>();
        
        queue.offer(head);
        visited.add(head);
        
        while (!queue.isEmpty()) {
            Point current = queue.poll();
            
            if (current.equals(food)) {
                return getFirstDirection(cameFrom, head, current);
            }
            
            for (int[] dir : DIRECTIONS) {
                Point next = new Point(current.x + dir[0], current.y + dir[1]);
                
                if (next.x >= 0 && next.x < gridWidth && 
                    next.y >= 0 && next.y < gridHeight &&
                    !obstacles.contains(next) && !visited.contains(next)) {
                    
                    visited.add(next);
                    cameFrom.put(next, current);
                    queue.offer(next);
                }
            }
        }
        return null;
    }
    
    private static Direction getFirstDirection(Map<Point, Point> cameFrom, 
                                                Point start, Point end) {
        Point current = end;
        while (cameFrom.get(current) != null && !cameFrom.get(current).equals(start)) {
            current = cameFrom.get(current);
        }
        
        if (current.x < start.x) return Direction.LEFT;
        if (current.x > start.x) return Direction.RIGHT;
        if (current.y < start.y) return Direction.UP;
        return Direction.DOWN;
    }
}

4.2 皮肤管理器

java 复制代码
import javax.swing.*;
import java.awt.*;
import java.util.*;

public class SkinManager {
    private static SkinManager instance;
    private List<Skin> skins = new ArrayList<>();
    private Skin currentSkin;
    private Random random = new Random();
    
    private SkinManager() {
        loadSkins();
        randomSkin();
    }
    
    public static SkinManager getInstance() {
        if (instance == null) {
            instance = new SkinManager();
        }
        return instance;
    }
    
    private void loadSkins() {
        skins.add(new Skin("经典", Color.RED, Color.GREEN, Color.ORANGE));
        skins.add(new Skin("暗黑", Color.CYAN, Color.DARK_GRAY, Color.YELLOW));
        skins.add(new Skin("森林", new Color(34, 139, 34), new Color(0, 100, 0), Color.ORANGE));
        skins.add(new Skin("海洋", Color.BLUE, new Color(0, 191, 255), Color.RED));
    }
    
    public void randomSkin() {
        currentSkin = skins.get(random.nextInt(skins.size()));
    }
    
    public Skin getCurrentSkin() { return currentSkin; }
    
    public static class Skin {
        public final Color headColor;
        public final Color bodyColor;
        public final Color foodColor;
        public final String name;
        
        public Skin(String name, Color head, Color body, Color food) {
            this.name = name;
            this.headColor = head;
            this.bodyColor = body;
            this.foodColor = food;
        }
    }
}

📸

五、常见问题

画面闪烁? 继承JPanel重写paintComponent,Swing自动启用双缓冲。

食物生成在蛇身上? 生成后遍历蛇身校验,重合则重新生成。

键盘响应不灵敏? 确保JPanel调用了setFocusable(true)。

六、总结

三个版本从简单到复杂:

· 初级版:200行,Swing数组实现,适合刚学完面向对象的同学

· 中级版:500行,JavaFX LinkedList实现,带计分暂停生命值

· 高级版:1000+行,BFS寻路AI、多套皮肤、本地排行榜

源码在文首三个地方都有,哪个方便用哪个。

最后问一句:你们Java课设交的啥?有没有被老师吐槽过?评论区唠唠。

我是郭wes,码龄三年,愿大家都发高质文章,一起加油。

相关推荐
John.Lewis2 小时前
C++进阶(12)附加学习:STL之空间配置器(了解)
开发语言·c++·笔记
IGAn CTOU2 小时前
王炸级更新!Spring Boot 3.4 正式发布,新特性真香!
java·spring boot·后端
C雨后彩虹2 小时前
最多等和不相交连续子序列
java·数据结构·算法·华为·面试
23471021272 小时前
4.16 学习笔记
开发语言·软件测试·python
tycooncool2 小时前
Spring中的IOC详解
java·后端·spring
014-code2 小时前
日志规范:怎么写才不算写废话
java·开发语言·设计模式·日志
Binarydog_Lee3 小时前
Rust 核心机制:所有权、借用与生命周期
开发语言·rust
XMYX-03 小时前
17 - Go 通道 Channel 底层原理 + 实战详解
开发语言·golang
CQU_JIAKE3 小时前
4.17[Q]
java·linux·服务器