前言:
今天姐妹们对“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); }}