Java贪吃蛇游戏

89 篇文章 0 订阅
23 篇文章 0 订阅

1、DemoApp

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.stage.Stage;

public class DemoApp extends Application {

    private static int x = 400;   // 绘制圆的 x
    private static int y = 300;   // 绘制圆的 y

    // 通过枚举(enum)实现方向,一类有限的可选的选项
    enum Direction {
        DIR_UP, DIR_LEFT, DIR_DOWN, DIR_RIGHT
    }
    private static Direction direction = Direction.DIR_LEFT;

    @Override
    public void start(Stage primaryStage) throws Exception {
        final Canvas canvas = new Canvas(800, 600);   // 画布对象
        Pane pane = new Pane();
        pane.getChildren().add(canvas);

        // 在画布上进行图形绘制
        final GraphicsContext gc = canvas.getGraphicsContext2D();     // 利用图形上下文,在画布上绘图
       
        //AnimationTimer timer = new MyTimer(canvas, gc);
        AnimationTimer timer = new AnimationTimer() {
            // 启动类中,可以直接使用局部变量中,被 final 修饰的,或者没被 final 修饰但也更改过的变量
            long lastTick;

            @Override
            public void handle(long now) {
                if (lastTick == 0 || now - lastTick > 1e9 / 48) {
                    lastTick = now;

                    gc.setFill(Color.WHITE);
                    gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());

                    gc.setFill(Color.BLACK);
                    gc.setFont(new Font(40));
                    gc.fillText("小游戏", 10, 40);

                    gc.setFill(Color.RED);
                    gc.fillOval(x, y, 50, 50);

                    switch (direction) {
                        case DIR_UP:
                            y--;
                            break;
                        case DIR_LEFT:
                            x--;
                            break;
                        case DIR_DOWN:
                            y++;
                            break;
                        case DIR_RIGHT:
                            x++;
                            break;
                    }
                }
            }
        };
        timer.start();  // 调用 start(),启动定时器

        Scene scene = new Scene(pane);

        // 为 scene 绑定键盘按下事件
        // 每当键盘上有 键 按下时,怎么办
        scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {
                //System.out.println(event.getEventType() + ":" + event.getCode());

                switch (event.getCode()) {
                    case W: case UP:
                        direction = Direction.DIR_UP;
                        break;
                    case A: case LEFT:
                        direction = Direction.DIR_LEFT;
                        break;
                    case S: case DOWN:
                        direction = Direction.DIR_DOWN;
                        break;
                    case D: case RIGHT:
                        direction = Direction.DIR_RIGHT;
                        break;
                }
            }
        });


        primaryStage.setScene(scene);

        primaryStage.setTitle("贪吃蛇");
        primaryStage.show();
    }

    public static void main(String[] args) {
        // launch: 引导;加载
        //Application.launch(SnakeApp.class, args);

        launch(args);
    }
}

2、SnackApp

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.stage.Stage;

import java.util.Arrays;
import java.util.Random;

// 主要采用面向过程的思考方式
public class SnakeApp extends Application {
    // 定义需要的变量
    // 定义游戏区域的大小,格子的宽和高
    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);
    // 蛇
    // 蛇的身体最多可以长到 1000 节
    private static Point[] snake = new Point[1000];
    // 现在蛇有多少节
    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 void newGame() {
        speed = 3;
        // 先把蛇回归到原始状态
        Arrays.fill(snake, null);
        snakeLength = 0;
        // 蛇初始形态有 3 节
        snake[snakeLength++] = new Point(WIDTH / 2, HEIGHT / 2);
        snake[snakeLength++] = new Point(WIDTH / 2, HEIGHT / 2);
        snake[snakeLength++] = new Point(WIDTH / 2, HEIGHT / 2);

        // 先有蛇,再有食物
        newFood();

        // 方向初始化成左
        direction = Direction.LEFT;

        // 游戏未结束
        gameOver = false;
    }

    // 用来在地图上随机生成食物
    private static void newFood() {
        // 1. 不能生成到地图外边
        //    x in [0, WIDTH)
        //    y in [0, HEIGHT)
        // 2. 不能和蛇的身体出现碰撞

        int x, 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;
    }

    // 其实就是一个遍历查找的逻辑
    private static boolean isCollision(int x, int y) {
        // 遍历蛇的每一节身体
        // 不要用 foreach 写法,因为数组中,不是每个位置都是有意义的
        // 只有 [0, snakeLength) 有意义
        for (int i = 0; i < snakeLength; i++) {
            Point point = snake[i];
            if (point.x == x && point.y == y) {
                return true;
            }
        }

        return false;
    }

    // 每一帧,要做的逻辑事宜
    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;
        }

        // 判断蛇走出游戏边界 —— 走出边界,游戏结束
        // head.x in [0, WIDTH) 有效
        // head.y in [0, HEIGHT) 有效
        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);
            // 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. 进行蛇的绘制 —— 每一块是一个矩形
        for (int i = 0; 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.RED);
            gc.setFont(new Font(20));
            gc.fillText("游戏结束,再接再厉!", 100, 100);
        }
    }

    @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) {
                if (gameOver) {
                    return;
                }

                if (lastTick == 0 || now - lastTick > 1e9 / speed) {
                    lastTick = now;
                    frame();
                    render(gc);
                }
            }
        };
        timer.start();

        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("贪吃蛇");
        primaryStage.setResizable(false);
        primaryStage.sizeToScene();
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

3、Point

public class Point {
    public int x;
    public int y;

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

执行结果(实际为动态效果)
在这里插入图片描述

贪吃蛇.jar import java.io.IOException; import java.util.Hashtable; import java.util.Vector; import javax.microedition.lcdui.Font; import javax.microedition.lcdui.Graphics; public class DCSnake extends i { private h jdField_a_of_type_H; private h jdField_b_of_type_H; private h jdField_c_of_type_H; private boolean jdField_c_of_type_Boolean; private boolean d; private int jdField_a_of_type_Int; private int jdField_b_of_type_Int; private int jdField_c_of_type_Int; private boolean e; private f jdField_a_of_type_F; private f jdField_b_of_type_F; private f jdField_c_of_type_F; public final void a(int paramInt) { if (!this.jdField_c_of_type_Boolean) { paramInt = this; if (!this.d) { paramInt.jdField_a_of_type_Int = 6; paramInt.jdField_b_of_type_Int = 0; paramInt.d = true; return; } if (paramInt.jdField_b_of_type_Int < paramInt.jdField_a_of_type_Int) { Object localObject1; switch (paramInt.jdField_b_of_type_Int) { case 0: a.a(); break; case 1: this = paramInt; try { localObject1 = null; localObject1 = Font.getFont(32, 0, 8); Font localFont1 = Font.getFont(32, 1, 8); Font localFont2 = Font.getFont(32, 1, 0); this.jdField_a_of_type_F = new f((Font)localObject1, 0, -1); this.jdField_b_of_type_F = new f(localFont1, 16777215, 0); this.jdField_c_of_type_F = new f(localFont2, 16776960, 32768); t.a(this.jdField_c_of_type_F, this.jdField_a_of_type_F, this.jdField_b_of_type_F); } catch (IOException localIOException) { } case 2: localObject1 = a.a(new int[] { 1245188, 1245190, 1245189 }); v[] arrayOfv = a.a(new int[] { 1245191, 1245193, 1245192 }); t.a(new c(localObject1, true), new c(arrayOfv, true)); break;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值