Java版贪吃蛇小游戏(保姆级:每步均有注释)

 源码:(复制麻烦格式化不了,代码txt版上面👆立即下载就行,无需会员提取码)

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import java.util.LinkedList;
import java.util.ListIterator;

/**
 * 蛇游戏的JavaFX实现类
 */
public class demo extends Application {
    // 游戏窗口的宽、高以及蛇和食物的单个单元尺寸
    private static final int WIDTH = 20;
    private static final int HEIGHT = 20;
    private static final int TILE_SIZE = 15;
    // 游戏是否结束的标志
    private static boolean isGameOver = false;
    // 蛇的身体部分
    private static LinkedList<Point> snake = new LinkedList<>();
    // 食物的位置
    private static Point food;
    // 蛇的当前方向
    private static Direction direction = Direction.RIGHT;
    // 绘图用的Canvas和GraphicsContext
    private Canvas canvas;
    private GraphicsContext gc;



    /**
     * 主程序入口。
     * 调用launch方法启动应用程序,传入命令行参数args。
     *
     * @param args 命令行参数数组,用于传递给应用程序的启动参数。
     */
    public static void main(String[] args) {
        launch(args);
    }



    /**
     * 程序的入口点,初始化游戏窗口并设置相关事件处理器。
     * @param primaryStage 主舞台,用于显示游戏界面。
     */
    @Override
    public void start(Stage primaryStage) {
        // 创建根节点,用于放置游戏画布
        Pane root = new Pane();
        // 初始化游戏画布,大小为棋盘的宽度和高度乘以单个方格的大小
        canvas = new Canvas(WIDTH * TILE_SIZE, HEIGHT * TILE_SIZE);
        // 将画布添加到根节点
        root.getChildren().add(canvas);

        // 创建场景,设置根节点为场景的内容
        Scene scene = new Scene(root);
        // 设置键盘事件处理器,用于控制蛇的移动方向
        // 设置键盘事件监听,用于改变蛇的方向
        scene.setOnKeyPressed(e -> {
            KeyCode code = e.getCode();
            // 根据按键方向更新蛇的移动方向,防止蛇撞墙
            if (code == KeyCode.UP && direction != Direction.DOWN) {
                direction = Direction.UP;
            } else if (code == KeyCode.DOWN && direction != Direction.UP) {
                direction = Direction.DOWN;
            } else if (code == KeyCode.LEFT && direction != Direction.RIGHT) {
                direction = Direction.LEFT;
            } else if (code == KeyCode.RIGHT && direction != Direction.LEFT) {
                direction = Direction.RIGHT;
            }
        });

        // 设置场景到主舞台,设置窗口标题,显示窗口
        primaryStage.setScene(scene);
        primaryStage.setTitle("Snake Game");
        primaryStage.show();

        // 初始化游戏状态
        // 初始化游戏
        initializeGame();
        // 启动游戏循环线程
        // 启动游戏循环
        new Thread(this::gameLoop).start();
    }


    /**
     * 初始化游戏设置。
     * 这个方法负责设置游戏的初始状态,包括蛇的位置、食物的生成以及获取图形上下文对象,为后续的游戏渲染做准备。
     */
    private void initializeGame() {
        // 初始化蛇的位置在屏幕中心
        snake.add(new Point(HEIGHT / 2, WIDTH / 2));
        // 生成游戏中的食物,为蛇提供可以增长的目标
        generateFood();
        // 获取Canvas的图形上下文对象,用于后续的绘图操作
        gc = canvas.getGraphicsContext2D();
    }


    private void resetGame() {
        // 清空蛇的身体,并在屏幕中心位置重新生成蛇的头部
        snake.clear();
        snake.add(new Point(HEIGHT / 2, WIDTH / 2));

        // 生成新的食物
        generateFood();

        // 设置初始移动方向为向右
        direction = Direction.RIGHT;

        // 重置游戏结束标志为未结束
        isGameOver = false;
    }



    /**
     * 主游戏循环。
     * 负责更新蛇的位置,检查碰撞,绘制游戏画面,并控制游戏的帧率。
     * 当游戏结束时,会弹出对话框询问玩家是否重新开始游戏。
     */
    private void gameLoop() {
        // 当游戏未结束时,持续进行游戏循环
        while (!isGameOver) {
            updateSnake();     // 更新蛇的位置
            checkCollision();  // 检查碰撞
            drawGame();        // 绘制游戏画面
            try {
                Thread.sleep(100); // 控制游戏帧率为每秒10帧
                // 控制游戏帧率,使游戏运行得更平滑
            } catch (InterruptedException e) {
                e.printStackTrace(); // 处理线程中断异常
            }
        }
        // 游戏结束后的处理
        Platform.runLater(() -> {
            Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
            alert.setTitle("Game Over");
            alert.setHeaderText("帥哥,想重新開一把貪吃蛇嗎?");
            alert.showAndWait().ifPresent(response -> {
                if (response == ButtonType.OK) {
                    resetGame(); // 重置游戏
                    Stage primaryStage = (Stage) canvas.getScene().getWindow();
                    start(primaryStage); // 重新开始游戏
                } else {
                    System.exit(0); // 退出游戏
                }
            });
        });
    }





    /**
     * 更新蛇的位置。
     * 根据当前蛇的头部位置计算下一个位置,将蛇的头部移动到新位置。
     * 如果新头部位置与食物位置相同,说明蛇吃到了食物,生成新的食物;
     * 否则,移除蛇的尾部,模拟蛇的身体移动。
     */
    private void updateSnake() {
        // 计算蛇的下一个头部位置
        Point newHead = calculateNextHead();
        // 将新头部位置添加到蛇的头部
        snake.addFirst(newHead);
        // 检查新头部是否与食物位置重合
        if (!newHead.equals(food)) {
            // 如果不重合,移除蛇的尾部
            snake.removeLast();
        } else {
            // 如果重合,生成新的食物
            generateFood();
        }
    }



    /**
     * 检查蛇是否与自身发生了碰撞。
     * 通过遍历蛇的身体部位,比较头部与除头部外的其他部位是否重合,来判断游戏是否结束。
     * 如果发生碰撞,则设置游戏结束标志为true。
     */
    private void checkCollision() {
        // 获取蛇的头部位置
        Point head = snake.getFirst();
        // 从蛇的第二个身体部位开始遍历,检查是否有与头部重合的情况
        for (int i = 1; i < snake.size(); i++) {
            // 如果找到重合部位,说明蛇撞到了自己,游戏结束
            if (head.equals(snake.get(i))) {
                isGameOver = true;
                return;
            }
        }
    }


    /**
     * 绘制游戏界面
     * 此方法负责更新游戏屏幕上的所有元素,包括背景、蛇、食物等。
     * 它使用图形上下文(gc)来绘制这些元素,通过对颜色和形状的控制,实现游戏的视觉表现。
     */
    private void drawGame() {
        // 设置背景颜色为黑色,并填充整个游戏区域
        gc.setFill(Color.BLACK);
        gc.fillRect(0, 0, WIDTH * TILE_SIZE, HEIGHT * TILE_SIZE);

        // 计算蛇的移动方向,用于后续绘制蛇的眼睛
        int direction = snake.getFirst().x - snake.getLast().x;
        // 设置食物的颜色为红色,并绘制在屏幕上
        // 绘制食物
        gc.setFill(Color.RED);
        gc.fillOval(food.y * TILE_SIZE, food.x * TILE_SIZE, TILE_SIZE, TILE_SIZE);

        // 设置蛇身体的颜色为粉色,并遍历蛇的身体部分进行绘制
        // 绘制蛇的身体
        gc.setFill(Color.PINK);
        ListIterator<Point> listIterator = snake.subList(1, snake.size()).listIterator();
        while(listIterator.hasNext()) {
            Point point = listIterator.next();
            gc.fillOval(point.y * TILE_SIZE, point.x * TILE_SIZE, TILE_SIZE, TILE_SIZE);
        }

        // 绘制蛇的头部,使用白色填充和白色边框,头部比身体稍大,有明显的区分
        // 绘制蛇的头部,使用不同的形状和颜色加以区分
        Point head = snake.getFirst();
        gc.setFill(Color.WHITE); // 头部颜色
        gc.setStroke(Color.WHITE); // 边框颜色
        gc.setLineWidth(2); // 边框宽度
        gc.fillOval(head.y * TILE_SIZE, head.x * TILE_SIZE, TILE_SIZE + 2, TILE_SIZE + 2); // 头部尺寸稍大
        gc.strokeOval(head.y * TILE_SIZE, head.x * TILE_SIZE, TILE_SIZE + 2, TILE_SIZE + 2);

        // 根据蛇的移动方向,绘制蛇的眼睛,增加蛇的立体感和生动性
        // 绘制蛇的眼睛
        gc.setFill(Color.BLACK);
        if (direction == 0) { // 左右移动
            gc.fillOval(head.y * TILE_SIZE + 8, head.x * TILE_SIZE + 5, 3, 3); // 上眼
            gc.fillOval(head.y * TILE_SIZE + 8, head.x * TILE_SIZE + 12, 3, 3); // 下眼
        } else { // 上下移动
            gc.fillOval(head.y * TILE_SIZE + 5, head.x * TILE_SIZE + 5, 3, 3); // 左眼
            gc.fillOval(head.y * TILE_SIZE + 12, head.x * TILE_SIZE + 5, 3, 3); // 右眼
        }
    }



    /**
     * 生成食物点的方法
     * 该方法用于在游戏区域内随机生成一个食物点,食物点的位置不能与蛇的身体位置重叠。
     * 随机生成坐标的过程中,如果生成的坐标与蛇的身体位置重叠,则重新生成,直到找到一个不重叠的位置。
     * 这样做是为了确保食物总是出现在蛇可以到达的区域,避免游戏无法进行下去的情况。
     */
    private void generateFood() {
        int x, y;
        // 生成随机坐标直到该坐标不与蛇的身体重叠
        do {
            x = (int) (Math.random() * HEIGHT);
            y = (int) (Math.random() * WIDTH);
        } while (snake.contains(new Point(x, y)));
        // 将生成的不与蛇重叠的坐标设置为食物的位置
        food = new Point(x, y);
    }



    /**
     * 计算蛇的下一个头部位置。
     * 根据当前蛇的朝向和屏幕的大小,计算出蛇移动后的新位置。
     * 蛇的移动是基于其当前头部位置和移动方向来确定的。
     * 通过取模运算确保蛇可以在屏幕边缘继续移动,实现环绕屏幕的效果。
     *
     * @return 返回一个新的Point对象,表示蛇的下一个头部位置。
     */
    private Point calculateNextHead() {
        // 获取蛇的头部位置
        Point head = snake.getFirst();
        // 解构头部位置的x和y坐标
        int x = head.x, y = head.y;
        // 根据当前方向调整x或y坐标,以计算出蛇的下一个位置
        switch (direction) {
            case UP:
                x--;
                break;
            case DOWN:
                x++;
                break;
            case LEFT:
                y--;
                break;
            case RIGHT:
                y++;
                break;
        }
        // 通过取模运算确保蛇可以在屏幕边缘继续移动,实现环绕屏幕的效果
        // 蛇可以环绕屏幕移动
        x = (x + HEIGHT) % HEIGHT;
        y = (y + WIDTH) % WIDTH;
        // 返回计算出的蛇的下一个头部位置
        return new Point(x, y);
    }


    // 定义点类,用于表示蛇身体上的各个点或食物的位置
    static class Point {
        int x;
        int y;

        Point(int x, int y) {
            this.x = x;
            this.y = y;
        }

        // 重写equals方法,用于判断两个点是否相同
        @Override
        public boolean equals(Object obj) {
            if (this == obj) return true;
            if (obj == null || getClass() != obj.getClass()) return false;
            Point point = (Point) obj;
            return x == point.x && y == point.y;
        }
    }

    // 定义蛇的移动方向枚举类型
    public enum Direction {
        UP, DOWN, LEFT, RIGHT
    }
}

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值