源码:(复制麻烦格式化不了,代码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
}
}