龙空技术网

基于JavaFX的贪吃蛇小游戏

Java机械师 53

前言:

今天姐妹们对“java实现防碰撞算法实验报告”大致比较珍视,咱们都想要剖析一些“java实现防碰撞算法实验报告”的相关知识。那么小编同时在网上网罗了一些对于“java实现防碰撞算法实验报告””的相关知识,希望看官们能喜欢,看官们一起来学习一下吧!

游戏背景介绍

贪吃蛇游戏是一款经典的小游戏,它的玩法很简单,就是控制蛇吃食物,每吃一个食物蛇的长度就会加一,直到蛇撞到墙壁或者撞到自己时游戏结束,最终的得分是蛇的长度减一。

JavaFX

用Java开发桌面端首选就是JavaFX,它的推出用来取代Swing(一个古老的Java桌面端框架)。

虽然都说Java开发桌面端性能不行,但是我们的Java开发工具IntelliJ IDEA的界面是由JavaFX构建的。最开始的我的世界(Minecraft)这款游戏是Java开发的,虽然没有使用Java标准GUI库(它自己的游戏引擎和自定义的用户界面),但也足以证明Java的魅力。

游戏规则初始时,蛇的长度为一,位于游戏界面的中心位置。每次随机生成一块食物,食物不能出现在蛇的身体上。蛇可以通过四个方向键上下左右移动,不能撞到墙壁或自己的身体。每吃一块食物,蛇的长度加一。穿过左边的墙壁,出现在右边;穿过上边的墙壁,出现在下面;反之亦然。游戏结束时,弹出得分对话框,点击重新开始新游戏。代码结构

本教程主要涉及的代码文件是SnakeGame.java,整个代码文件的框架如下:

java复制代码import java.util.ArrayDeque;import java.util.Deque;import javafx.animation.AnimationTimer;import javafx.application.Application;import javafx.geometry.Pos;import javafx.scene.Scene;import javafx.scene.canvas.Canvas;import javafx.scene.canvas.GraphicsContext;import javafx.scene.control.Alert;import javafx.scene.control.Alert.AlertType;import javafx.scene.input.KeyCode;import javafx.scene.layout.StackPane;import javafx.stage.Stage;public class SnakeGame extends Application {    // 游戏界面的宽度    private static final int WIDTH = 20;    // 游戏界面的高度    private static final int HEIGHT = 20;    // 每个格子的大小    private static final int SIZE = 20;    // 蛇的速度    private static final int SPEED = 5;    // 蛇的身体    private Deque<Point> snake = new ArrayDeque<>();    // 蛇的初始方向    private Direction direction = Direction.RIGHT;    // 食物的位置    private Point food;    // 游戏是否结束    private boolean gameOver = false;    // 游戏是否暂停    private boolean gamePaused = false;    @Override    public void start(Stage primaryStage) throws Exception {        // 界面初始化        // ...        // 初始化游戏        // ...        // 动画循环        // ...    }    // 界面初始化方法    private void initGUI() {        // ...    }    // 初始化游戏方法    private void initGame() {        // ...    }    // 蛇的移动方法    private void move() {        // ...    }    // 检测碰撞方法    private void checkCollision() {        // ...    }    // 生成食物方法    private void generateFood() {        // ...    }    // 绘制游戏画面方法    private void paint(GraphicsContext gc) {        // ...    }    // 显示游戏结束对话框方法    private void showGameOverDialog() {        // ...    }    // 方向枚举类    private enum Direction {        UP, DOWN, LEFT, RIGHT    }    // 坐标点类    private static class Point {        private int x;        private int y;        public Point(int x, int y) {            this.x = x;            this.y = y;        }        public int getX() {            return x;        }        public int getY() {            return y;        }        @Override        public boolean equals(Object o) {            // ...        }        @Override        public int hashCode() {            // ...        }    }    public static void main(String[] args) {        launch(args);    }}
逻辑分析

在实现贪吃蛇游戏之前,我们需要先了解一下游戏的逻辑。

在游戏界面内,不断地移动蛇的位置。蛇的移动方向可以通过键盘上的上下左右四个方向键来控制。当蛇头碰到边界或碰到自己的身体时,游戏结束。当蛇头碰到食物时,就会吃掉食物,长度加1,随后继续向前移动。吃掉食物后,会重新生成一个新的食物,判断新食物的位置是否和已有的蛇的位置冲突。实现步骤

下面分步骤进行实现,每一个步骤都结合代码,逻辑清晰。

步骤1:界面初始化

在start方法中进行界面的初始化,包括创建Canvas、GraphicsContext等,并将Canvas添加到StackPane作为根节点,最后显示舞台。代码如下:

java复制代码@Overridepublic void start(Stage primaryStage) throws Exception {    // 创建Canvas    Canvas canvas = new Canvas(WIDTH * SIZE, HEIGHT * SIZE);    GraphicsContext gc = canvas.getGraphicsContext2D();    // 创建根节点    StackPane root = new StackPane(canvas);    root.setAlignment(Pos.CENTER);    // 创建场景    Scene scene = new Scene(root);    scene.setOnKeyPressed(event -> {        KeyCode keyCode = event.getCode();        switch (keyCode) {            // ...        }    });    // 显示舞台    primaryStage.setScene(scene);    primaryStage.setTitle("贪吃蛇游戏");    primaryStage.setResizable(false);    primaryStage.show();}
步骤2:初始化游戏

在游戏开始前,需要初始化一些参数,包括蛇的位置、食物位置、游戏状态等。具体实现代码如下:

java复制代码// 初始化游戏方法private void initGame() {    // 清空蛇的身体    snake.clear();    // 在游戏界面的中心生成蛇头    int x = WIDTH / 2;    int y = HEIGHT / 2;    snake.add(new Point(x, y));    // 生成食物    generateFood();    // 初始化游戏状态    gameOver = false;    gamePaused = false;}
步骤3:蛇的移动

在游戏中,蛇可以通过键盘上的上下左右四个方向键来控制移动方向。我们可以在Scene的按键监听事件中实现,根据按下的方向键修改蛇的移动方向。具体代码实现如下:

java复制代码// Scene的按键监听事件scene.setOnKeyPressed(event -> {    KeyCode keyCode = event.getCode();    switch (keyCode) {        case UP:            if (direction != Direction.DOWN) {                direction = Direction.UP;            }            break;        case DOWN:            if (direction != Direction.UP) {                direction = Direction.DOWN;            }            break;        case LEFT:            if (direction != Direction.RIGHT) {                direction = Direction.LEFT;            }            break;        case RIGHT:            if (direction != Direction.LEFT) {                direction = Direction.RIGHT;            }            break;        case P:            gamePaused = !gamePaused;            break;        case R:            initGame();            break;        default:            break;    }});

在每次动画循环中,根据蛇的移动方向来计算移动后的新位置。如果新位置在蛇的身体上或者超出了边界,就说明游戏结束了。判断蛇是否吃到了食物,如果吃到了就让蛇的身体变长,并在新位置生成一个新的食物。

java复制代码// 蛇的移动方法private void move() {    Point head = snake.getFirst();    Point newHead = null;    switch (direction) {        case UP:            newHead = new Point(head.getX(), head.getY() - 1);            break;        case DOWN:            newHead = new Point(head.getX(), head.getY() + 1);            break;        case LEFT:            newHead = new Point(head.getX() - 1, head.getY());            break;        case RIGHT:            newHead = new Point(head.getX() + 1, head.getY());            break;        default:            break;    }    // 判断是否撞到自己的身体    if (snake.contains(newHead)) {        gameOver = true;        showGameOverDialog();        return;    }    // 判断是否撞到墙壁    if (newHead.getX() < 0 || newHead.getX() >= WIDTH ||            newHead.getY() < 0 || newHead.getY() >= HEIGHT) {        gameOver = true;        showGameOverDialog();        return;    }    // 更新蛇的位置    snake.addFirst(newHead);    // 判断是否吃到了食物    if (newHead.equals(food)) {        // 如果吃到了食物,就让蛇的身体变长        generateFood();    } else {        // 如果没有吃到食物,就让蛇的尾巴消失        snake.removeLast();    }}
步骤4:检测碰撞

在每次蛇的移动后,需要检测蛇是否撞到了自己的身体。如果撞到了,说明游戏结束了。具体代码实现如下:

java复制代码// 检测碰撞方法private void checkCollision() {    Point head = snake.getFirst();    for (Point point : snake) {        if (point != head && point.equals(head)) {            gameOver = true;            showGameOverDialog();            break;        }    }}
步骤5:生成食物

每个食物都是在游戏界面上随机出现的,食物不能出现在蛇的身体上。生成食物时,可以使用do-while循环来判断是否有重合的情况。具体代码实现如下:

java复制代码// 生成食物方法private void generateFood() {    boolean validPosition;    int x, y;    do {        validPosition = true;        x = (int) (Math.random() * WIDTH);        y = (int) (Math.random() * HEIGHT);        for (Point point : snake) {            if (point.getX() == x && point.getY() == y) {                validPosition = false;                break;            }        }    } while (!validPosition);    food = new Point(x, y);}
步骤6:绘制游戏画面

在Canvas上通过GraphicsContext绘制蛇、食物等游戏元素,实现游戏的画面。具体代码实现如下:

java复制代码// 绘制游戏画面方法private void paint(GraphicsContext gc) {    // 清空画布    gc.clearRect(0, 0, WIDTH * SIZE, HEIGHT * SIZE);    // 绘制蛇身    gc.setFill(javafx.scene.paint.Color.GREEN);    for (Point point : snake) {        gc.fillRect(point.getX() * SIZE, point.getY() * SIZE, SIZE, SIZE);    }    // 绘制头部    gc.setFill(javafx.scene.paint.Color.DARKGREEN);    Point head = snake.getFirst();    gc.fillRect(head.getX() * SIZE, head.getY() * SIZE, SIZE, SIZE);    // 绘制食物    gc.setFill(javafx.scene.paint.Color.RED);    gc.fillRect(food.getX() * SIZE, food.getY() * SIZE, SIZE, SIZE);}
步骤7:显示游戏结束对话框

当游戏结束时,弹出得分对话框,点击重新开始新游戏。具体代码实现如下:

java复制代码// 显示游戏结束对话框方法private void showGameOverDialog() {    Alert alert = new Alert(AlertType.INFORMATION);    alert.setTitle("游戏结束");    alert.setHeaderText(null);    alert.setContentText("游戏结束,您的得分是:" + (snake.size() - 1));    alert.show();    alert.setOnHidden(event -> {        initGame();    });}

至此,贪吃蛇游戏的实现已经完成了。

完整代码如下:

java复制代码package org.example;import javafx.animation.AnimationTimer;import javafx.application.Application;import javafx.geometry.Pos;import javafx.scene.Scene;import javafx.scene.canvas.Canvas;import javafx.scene.canvas.GraphicsContext;import javafx.scene.control.Alert;import javafx.scene.control.Alert.AlertType;import javafx.scene.input.KeyCode;import javafx.scene.layout.StackPane;import javafx.stage.Stage;import java.util.ArrayDeque;import java.util.Deque;public class SnakeGame extends Application {    private static final int WIDTH = 20; // 游戏界面的宽度    private static final int HEIGHT = 20; // 游戏界面的高度    private static final int SIZE = 20; // 每个格子的大小    private static final int SPEED = 5; // 蛇的速度    private Deque<Point> snake = new ArrayDeque<>(); // 蛇的身体    private Direction direction = Direction.RIGHT; // 蛇的初始方向    private Point food; // 食物的位置    private boolean gameOver = false; // 游戏是否结束    private boolean gamePaused = false; // 游戏是否暂停    @Override    public void start(Stage primaryStage) throws Exception {        Canvas canvas = new Canvas(WIDTH * SIZE, HEIGHT * SIZE);        GraphicsContext gc = canvas.getGraphicsContext2D();        StackPane root = new StackPane(canvas);        root.setAlignment(Pos.CENTER);        Scene scene = new Scene(root);        scene.setOnKeyPressed(event -> {            KeyCode keyCode = event.getCode();            switch (keyCode) {                case UP:                    if (direction != Direction.DOWN) {                        direction = Direction.UP;                    }                    break;                case DOWN:                    if (direction != Direction.UP) {                        direction = Direction.DOWN;                    }                    break;                case LEFT:                    if (direction != Direction.RIGHT) {                        direction = Direction.LEFT;                    }                    break;                case RIGHT:                    if (direction != Direction.LEFT) {                        direction = Direction.RIGHT;                    }                    break;                case P:                    gamePaused = !gamePaused;                    break;                case R:                    initGame();                    break;                default:                    break;            }        });        primaryStage.setScene(scene);        primaryStage.setTitle("贪吃蛇游戏");        primaryStage.setResizable(false);        primaryStage.show();        initGame();        new AnimationTimer() {            private long lastUpdateTime;            @Override            public void handle(long now) {                if (now - lastUpdateTime >= 1_000_000_000 / SPEED) { // 调整蛇的速度                    lastUpdateTime = now;                    if (!gameOver && !gamePaused) {                        move();                        checkCollision();                        paint(gc);                    }                }            }        }.start();    }    // 初始化游戏    private void initGame() {        snake.clear();        snake.add(new Point(WIDTH / 2, HEIGHT / 2));        generateFood();        gameOver = false;        gamePaused = false;    }    // 蛇的移动    private void move() {        Point head = snake.getFirst();        Point newHead = null;        switch (direction) {            case UP:                newHead = new Point(head.getX(), head.getY() - 1);                break;            case DOWN:                newHead = new Point(head.getX(), head.getY() + 1);                break;            case LEFT:                newHead = new Point(head.getX() - 1, head.getY());                break;            case RIGHT:                newHead = new Point(head.getX() + 1, head.getY());                break;            default:                break;        }        // 判断是否撞到自己的身体        if (snake.contains(newHead)) {            gameOver = true;            showGameOverDialog();            return;        }        // 判断是否撞到墙壁        if (newHead.getX() < 0 || newHead.getX() >= WIDTH ||                newHead.getY() < 0 || newHead.getY() >= HEIGHT) {            gameOver = true;            showGameOverDialog();            return;        }        snake.addFirst(newHead);        if (newHead.equals(food)) {            generateFood();        } else {            snake.removeLast();        }    }    // 检测碰撞    private void checkCollision() {        Point head = snake.getFirst();        for (Point point : snake) {            if (point != head && point.equals(head)) {                gameOver = true;                showGameOverDialog();                break;            }        }    }    // 生成食物    private void generateFood() {        boolean validPosition;        int x, y;        do {            validPosition = true;            x = (int) (Math.random() * WIDTH);            y = (int) (Math.random() * HEIGHT);            for (Point point : snake) {                if (point.getX() == x && point.getY() == y) {                    validPosition = false;                    break;                }            }        } while (!validPosition);        food = new Point(x, y);    }    // 绘制游戏画面    private void paint(GraphicsContext gc) {        // 清空画布        gc.clearRect(0, 0, WIDTH * SIZE, HEIGHT * SIZE);        // 绘制蛇身        gc.setFill(javafx.scene.paint.Color.GREEN);        for (Point point : snake) {            gc.fillRect(point.getX() * SIZE, point.getY() * SIZE, SIZE, SIZE);        }        // 绘制头部        gc.setFill(javafx.scene.paint.Color.DARKGREEN);        Point head = snake.getFirst();        gc.fillRect(head.getX() * SIZE, head.getY() * SIZE, SIZE, SIZE);        // 绘制食物        gc.setFill(javafx.scene.paint.Color.RED);        gc.fillRect(food.getX() * SIZE, food.getY() * SIZE, SIZE, SIZE);    }    // 显示游戏结束对话框    private void showGameOverDialog() {        Alert alert = new Alert(AlertType.INFORMATION);        alert.setTitle("游戏结束");        alert.setHeaderText(null);        alert.setContentText("游戏结束,您的得分是:" + (snake.size() - 1));        alert.show();        alert.setOnHidden(event -> {            initGame(); // 游戏结束后重新开始游戏        });    }    // 方向枚举类    private enum Direction {        UP, DOWN, LEFT, RIGHT    }    // 坐标点类    private static class Point {        private int x;        private int y;        public Point(int x, int y) {            this.x = x;            this.y = y;        }        public int getX() {            return x;        }        public int getY() {            return y;        }        @Override        public boolean equals(Object o) {            if (this == o) return true;            if (o == null || getClass() != o.getClass()) return false;            Point point = (Point) o;            return x == point.x && y == point.y;        }        @Override        public int hashCode() {            return x * 31 + y;        }    }    public static void main(String[] args) {        launch(args);    }}

标签: #java实现防碰撞算法实验报告 #基于java的贪吃蛇游戏的设计与实现