【Java实现一个小游戏】贪吃蛇游戏详细解析

实现效果

游戏开始:
【Java实现一个小游戏】贪吃蛇游戏详细解析_第1张图片
【Java实现一个小游戏】贪吃蛇游戏详细解析_第2张图片

游戏结束:显示得分
【Java实现一个小游戏】贪吃蛇游戏详细解析_第3张图片

准备阶段

首先准备Java开发环境以及对应用到的Jar包。
需借助JavaFX的图形库。

目录结构

【Java实现一个小游戏】贪吃蛇游戏详细解析_第4张图片

代码分析

Point类如下:
package snake;

/**
 * @ClassName Point
 * @Description :TODO
 * @Author Josvin
 * @Date 2021/02/04/14:08
 */
public class Point {
     
    public int x;
    public int y;
    public Point(int x, int y) {
     
        this.x = x;
        this.y = y;
    }


}

具体来看SnakeApp的实现:

  • 定义需要的变量(具体见代码注释)
// 定义需要的变量
    // 定义游戏区域的大小,宽和高各多少格子
    private static final int WIDTH = 20;
    private static final int HEIGHT = 20;

    // 绘制图形的时候,每个格子所占的像素
    private static final int GRID_SIZE = 40;

    //画布的宽和高
    private static final int CANVAS_WIDTH = WIDTH * GRID_SIZE;
    private static final int CANVAS_HEIGHT = HEIGHT * GRID_SIZE;


    // 定义游戏本身
    // 的移动速度
    // speed : 每秒移动的帧数
    // 帧数越高速度越快
    private static int speed;

    // 定义食物
    private static Point food = new Point(-1, -1);

    // 定义
    // 的长度最长就是沾满整个画布,=长*宽
    private static Point[] snake = new Point[WIDTH * HEIGHT];
    //现在的身体有多少节
    private static int snakeLength = 0;

    // 蛇现在的朝向(用枚举的方式列出四个状态)

    enum Direction {
     
        UP, LEFT, DOWN, RIGHT
    }

    private static Direction direction;

    // 构建一个随机生成器,用于生成食物的位置

    private static Random random = new Random();
    // 游戏结束标志位

    private static boolean gameOver;

    // 游戏得分
    private static int score;
  • 生成食物,也就是生成一个食物的位置,后边在渲染即可
// 用来在画布上随机生成食物
    private static void newFood() {
     
        // 1、 不能生成到画布外边
        //      x of [0,WIDTH)
        //      y of [0,HEIGHT)
        // 2、不能和 的身体出现碰撞

        int x;
        int y;
        do {
     
            x = random.nextInt(WIDTH);///x 的范围一定是 [0, WIDTH)
            y = random.nextInt(HEIGHT);///y 的范围一定是 [0,HEIGHT)
        } while (isCollision(x, y));
        // isCollision (x,y)返回true ,代表和的身体有碰撞
        // 赋值
        food.x = x;
        food.y = y;
    }
  • 实现isCollision (x,y)返回true ,代表和的身体有碰撞
private static boolean isCollision(int x, int y) {
     
        for (int i = 0; i < snakeLength; i++) {
     
            Point point = snake[i];
            if (point.x == x && point.y == y) {
     
                // 说明有冲突,则需要重新生成食物位置
                return true;
            }
        }
        return false;
    }
  • 蛇的移动也就是给snake数组赋值,标记蛇的位置(有一下情况)

    • 蛇走到了画布边界----游戏结束
    • 蛇碰到自己-----游戏结束
    • 蛇吃到食物----长度加1,速度提高,并且得分
// 下来处理每一帧,要做的事情
    private static void frame() {
     
        // 移动蛇 -- 移动身体
        // 注意从后往前,后边的总放在前边的一个位置
        for (int i = snakeLength - 1; i >= 1; i--) {
     
            snake[i].x = snake[i - 1].x;
            snake[i].y = snake[i - 1].y;
        }

        // 移动蛇 -- 移动蛇头
        Point head = snake[0];
        switch (direction) {
     
            case UP:
                head.y--;
                break;
            case LEFT:
                head.x--;
                break;
            case DOWN:
                head.y++;
                break;
            case RIGHT:
                head.x++;
                break;
        }

        // 判断蛇是否走出游戏边界,走出的游戏结束!
        if (head.x < 0 || head.x >= WIDTH || head.y < 0 || head.y >= HEIGHT) {
     
            gameOver = true;
            return;
        }

        // 判断蛇有没有碰到自己的身体其他位置 —— 碰到了,游戏结束
        for (int i = 1; i < snakeLength; i++) {
     
            Point point = snake[i];
            if (head.x == point.x && head.y == point.y) {
     
                gameOver = true;
                return;
            }
        }

        // 判断有没有吃到食物
        if (head.x == food.x && head.y == food.y) {
     
            // 如果吃到了食物
            // 1. 蛇的身体增加一节
            // 坐标随便,只要不影响绘图,因为随着下一帧到来,蛇移动之后,坐标就正确了
            snake[snakeLength++] = new Point(-1, -1);
            // 根据速度大小得分
            score = score + speed * 2;
            // 2. 重新生成新的食物
            newFood();
            // 3. 让速度增加
            speed++;
        }
    }
  • 渲染界面(每次需要对游戏界面擦除在渲染,其中蛇头需要单独渲染,并对游戏是否结束进行判断,如果结束渲染结束界面)
 // 每一帧要做的绘制工作 -- render(渲染)
    private  static void  render(GraphicsContext gc) {
     
        // 1. 通过重新绘制背景,擦除游戏界面
        gc.setFill(Color.BLACK);
        gc.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

        // 2. 进行蛇的绘制 —— 每一块是一个矩形
        // 蛇头单独渲染
        gc.setFill(Color.DARKVIOLET);
        // 矩形大小是 GRID_SIZE - 2;上下左右各控一个像素
        gc.fillRect(snake[0].x * GRID_SIZE + 1, snake[0].y * GRID_SIZE + 1, GRID_SIZE - 2, GRID_SIZE - 2);

        for (int i = 1; i < snakeLength; i++) {
     
            Point point = snake[i];
            gc.setFill(Color.GREEN);
            // 矩形大小是 GRID_SIZE - 2;上下左右各控一个像素
            gc.fillRect(point.x * GRID_SIZE + 1, point.y * GRID_SIZE + 1, GRID_SIZE - 2, GRID_SIZE - 2);
        }

        // 3. 进行食物的绘制 —— 圆形
        gc.setFill(Color.YELLOW);
        gc.fillOval(food.x * GRID_SIZE, food.y * GRID_SIZE, GRID_SIZE, GRID_SIZE);

        // 4. 进行游戏结束的绘制
        if (gameOver) {
     
            gc.setFill(Color.BLACK);
            gc.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
            gc.setFill(Color.CORNSILK);
            // 矩形大小是 GRID_SIZE - 2;上下左右各控一个像素
            gc.fillRect(WIDTH*GRID_SIZE/2 - 30*5, HEIGHT*GRID_SIZE/4, 300, 80);
            gc.setFill(Color.RED);
            gc.setFont(new Font(20));
            gc.fillText("最终游戏得分: "+score, WIDTH * GRID_SIZE / 2 - 30 * 3, HEIGHT * GRID_SIZE / 4 + 30);
            gc.fillText("游戏结束,再接再厉!", WIDTH * GRID_SIZE / 2 - 30 * 3, HEIGHT * GRID_SIZE / 4 + 60);

        }
    }
  • 重写start,完成游戏初始化,启动定时器,绑定键盘按下事件
@Override
    public void start(Stage primaryStage) throws Exception {
     
        // 进行游戏初始化
        newGame();

        // 面板对象
        Pane pane = new Pane();
        // 画布对象
        Canvas canvas = new Canvas(CANVAS_WIDTH, CANVAS_HEIGHT);
        pane.getChildren().add(canvas);
        // 场景对象
        Scene scene = new Scene(pane);

        // 在画布上进行图形绘制

        final GraphicsContext gc = canvas.getGraphicsContext2D();
        AnimationTimer timer = new AnimationTimer() {
     

            long lastTick;

            @Override
            public void handle(long now) {
     
                // now :以纳秒为单位,本次调用handle的时间
                if (gameOver) {
     
                    return;
                }
                if (lastTick == 0 || now - lastTick > 1e9 / speed) {
     
                    lastTick = now;
                    frame();
                    render(gc);
                }
            }
        };
        // 调用 start(),启动定时器
        timer.start();
        // 为 scene 绑定键盘按下事件
        // 每当键盘上有 键 按下时,怎么办
        scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
     
            @Override
            public void handle(KeyEvent event) {
     
                switch (event.getCode()) {
     
                    case W:
                    case UP:
                        if (direction != Direction.DOWN) {
     
                            direction = Direction.UP;
                        }
                        break;
                    case A:
                    case LEFT:
                        if (direction != Direction.RIGHT) {
     
                            direction = Direction.LEFT;
                        }
                        break;
                    case S:
                    case DOWN:
                        if (direction != Direction.UP) {
     
                            direction = Direction.DOWN;
                        }
                        break;
                    case D:
                    case RIGHT:
                        if (direction != Direction.LEFT) {
     
                            direction = Direction.RIGHT;
                        }
                        break;
                    case R:
                        if (gameOver) {
     
                            newGame();
                        }
                        break;
                }

            }
        });
        primaryStage.setScene(scene);
        primaryStage.setTitle("贪吃蛇小游戏  by Josvin");
        primaryStage.setResizable(false);
        primaryStage.sizeToScene();
        primaryStage.show();
    }
  • main方法:
public static void main(String[] args) {
     
        // launch: 引导;加载
        //Application.launch(SnakeApp.class, args);
        launch(args);
    }

Github仓库地址:贪吃蛇游戏代码

你可能感兴趣的:(Java,游戏,java,canvas,贪吃蛇)