JavaFX 过时了吗?你怎么看,闲暇之余实现一版贪吃蛇小游戏,放松一下!

关于JavaFX技术这里不做过多介绍,简单说一句:

JavaFx平台作为Java gui的替代方案,是一个富客户端平台解决方案,它能够使用应用程序开发人员轻松的创建跨平台的富客户端应用程序。

通过JavaFX实现简单的贪吃蛇游戏,没有小蛇素材,使用老鼠代替😊😊😊

实现说明

  • W S A D 代表上下左右方向
  • AnchorPane 主面板,锚点类型
  • Canvas 绘图对象,获取画笔进行图像显示绘制,设置位置坐标和尺寸
  • 音乐播放使用 MediaPlayer
  • 食物随机生成,使用子线程和阻塞队列进行生产。
  • 使用Timer 实现蛇前进和速度控制

核心对象

  1. Food: 食物对象
  2. Snake: 蛇对象
  3. SnakeHead: 蛇头对象
  4. SnakePart: 蛇身体一部分
  5. SnakeRun: 游戏启动类继承了javafx的Application

运行截图

image.png
image.png
image.png
image.png

代码实现

Food

package com.example.snake;

import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;

import java.util.Random;

public class Food {
    private final int x;
    private final int y;
    private final int size;
    private final Color color;
    private final GraphicsContext graphics;

    public Food(int x, int y, int size, Color color, GraphicsContext graphics) {
        this.x = x;
        this.y = y;
        this.size = size;
        this.color = color;
        this.graphics = graphics;
    }

    public Food(int x, int y, GraphicsContext graphics) {
        this.x = x;
        this.y = y;
        this.size = SnakePart.DEF_LEN;
        Random random = new Random();
        this.color = Color.rgb(random.nextInt(255), random.nextInt(255), random.nextInt(255));
        this.graphics = graphics;
    }

    public Food show() {
        graphics.setFill(color);
//        graphics.fillRect(x, y, size, size);
        graphics.drawImage(Caches.mouseImage2, x, y, size, size);

        return this;
    }

    public void vanish() {
        graphics.clearRect(x, y, size, size);
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public int getSize() {
        return size;
    }

    public Color getColor() {
        return color;
    }
}

SnakePart

package com.example.snake;

import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;

import java.util.Random;

/**
 * 身体片段
 */
public class SnakePart {
    public static final int DEF_LEN = 40;
    protected int x;
    protected int y;
    protected final int len;
    protected final Color color;
    protected final GraphicsContext graphics;

    public SnakePart(int x, int y, GraphicsContext graphics) {
        this.x = x;
        this.y = y;
        this.len = DEF_LEN;
        Random random = new Random();
        this.color = Color.rgb(random.nextInt(255), random.nextInt(255), random.nextInt(255));
        this.graphics = graphics;
    }

    public SnakePart(int x, int y, Color color, GraphicsContext graphics) {
        this.x = x;
        this.y = y;
        this.len = DEF_LEN;
        this.color = color;
        this.graphics = graphics;
    }
    public SnakePart(int x, int y, int len, Color color, GraphicsContext graphics) {
        this.x = x;
        this.y = y;
        this.len = len;
        this.color = color;
        this.graphics = graphics;
    }

    public void remove() {
        graphics.clearRect(x, y, len, len);
    }

    public SnakePart move(int dX, int dY) {
        x += dX;
        y += dY;
        graphics.drawImage(Caches.snakeBodyImage, x, y, len, len);
        return this;
    }
    public SnakePart updateLocation(int pX, int pY) {
        x = pX;
        y = pY;
        graphics.drawImage(Caches.snakeBodyImage, x, y, len, len);
        return this;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public int getLen() {
        return len;
    }

    @Override
    public String toString() {
        return "SnakePart{" +
                "x=" + x +
                ", y=" + y +
                ", color=" + color +
                '}';
    }
}

SnakeHead

package com.example.snake;

import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;

public class SnakeHead extends SnakePart {
    public SnakeHead(int x, int y, GraphicsContext graphics) {
        super(x, y, graphics);
    }

    public SnakeHead(int x, int y, Color color, GraphicsContext graphics) {
        super(x, y, color, graphics);
    }

    public SnakeHead(int x, int y, int len, Color color, GraphicsContext graphics) {
        super(x, y, len, color, graphics);
    }

    @Override
    public SnakePart move(int dX, int dY) {
        x += dX;
        y += dY;
//        graphics.setFill(color);
        graphics.drawImage(Caches.snakeHeadImage, x, y, len, len);
        return this;
    }

    @Override
    public SnakePart updateLocation(int pX, int pY) {
        x = pX;
        y = pY;
//        graphics.setFill(color);
        graphics.drawImage(Caches.snakeHeadImage, x, y, len, len);
        return this;
    }
}

Snake

package com.example.snake;

import javafx.application.Platform;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;

import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;

public class Snake {
    private int initX;
    private int initY;
    private int initLen;
    private int speed;
    //方向 w s a d -> 上下左右
    private KeyCode direction;
    private SnakeHead head;
    private List<SnakePart> body;
    private GraphicsContext graphics;
    private Canvas canvas;
    private ArrayBlockingQueue<Food> foodQueue;
    private Timer timer;
    private SnakeEvent snakeEvent;


    public Snake(int initX, int initY, int initLen, int speed, Canvas canvas, ArrayBlockingQueue<Food> foodQueue) {
        this.initX = initX;
        this.initY = initY;
        this.initLen = initLen;
        this.speed = speed;
        this.canvas = canvas;
        this.foodQueue = foodQueue;
        this.graphics = canvas.getGraphicsContext2D();
    }

    public void init() {
        head = new SnakeHead(initX, initY, graphics);
        body = new ArrayList<>();
        for (int i = 0; i < initLen; i++) {
            body.add(new SnakePart(initX - SnakePart.DEF_LEN * (i + 1), initY, graphics).move(0, 0));
        }
        timer = new Timer();
        direction = KeyCode.D;
    }

    public void turn(KeyEvent keyEvent) {
        KeyCode code = keyEvent.getCode();
        if (code != KeyCode.W && code != KeyCode.S && code != KeyCode.A && code != KeyCode.D) {
            return;
        }

        if (code == KeyCode.W && direction == KeyCode.S) {
            return;
        }
        if (code == KeyCode.S && direction == KeyCode.W) {
            return;
        }
        if (code == KeyCode.A && direction == KeyCode.D) {
            return;
        }
        if (code == KeyCode.D && direction == KeyCode.A) {
            return;
        }
        direction = code;

    }

    public boolean eat() {
        Food food = foodQueue.peek();
        if (food == null) {
            return false;
        }
        if (head.getX() == food.getX() && head.getY() == food.getY()) {
            food = foodQueue.poll();
            if (food == null) {
                return false;
            }
            SnakePart tail = body.get(body.size() - 1);
            if (direction == KeyCode.W) {
                body.add(new SnakePart(tail.getX(), tail.getY() + SnakePart.DEF_LEN, food.getColor(), graphics).move(0, 0));
            } else if (direction == KeyCode.S) {
                body.add(new SnakePart(tail.getX(), tail.getY() - SnakePart.DEF_LEN, food.getColor(), graphics).move(0, 0));
            } else if (direction == KeyCode.A) {
                body.add(new SnakePart(tail.getX() + SnakePart.DEF_LEN, tail.getY(), food.getColor(), graphics).move(0, 0));
            } else if (direction == KeyCode.D) {
                body.add(new SnakePart(tail.getX() - SnakePart.DEF_LEN, tail.getY(), food.getColor(), graphics).move(0, 0));
            }
            food.vanish();
            return true;
        }
        return false;
    }

    public void remove() {
        for (SnakePart snakePart : body) {
            snakePart.remove();
        }
    }

    public void move(int dX, int dY) {
        if (dX == 0 && dY == 0) {
            return;
        }
        remove();
        for (int i = body.size() - 1; i > 0; i--) {
            body.get(i).updateLocation(body.get(i - 1).getX(), body.get(i - 1).getY());
        }
        int headX = head.getX();
        int headY = head.getY();
        head.remove();
        body.get(0).updateLocation(headX, headY);
        head.move(dX, dY);

    }

    public void run() {
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                Platform.runLater(()->{
                    die();
                    eat();
                    switch (direction) {
                        case W: {
                            move(0, -SnakePart.DEF_LEN);
                            break;
                        }
                        case S: {
                            move(0, SnakePart.DEF_LEN);
                            break;
                        }
                        case A: {
                            move(-SnakePart.DEF_LEN, 0);
                            break;
                        }
                        case D: {
                            move(SnakePart.DEF_LEN, 0);
                            break;
                        }
                    }
                });

            }
        }, 0, 2000 / speed);
    }

    public void die() {
        double width = canvas.getWidth();
        double height = canvas.getHeight();
        if (head.getX() < 0 || head.getY() < 0 ||
                (head.getX() + SnakePart.DEF_LEN) > width ||
                (head.getY() + SnakePart.DEF_LEN) > height) {
            timer.cancel();
            EventType die = EventType.DIE;
            die.getData().setScore((body.size() - initLen) * 5);
            die.getData().setSnake(this);
            Platform.runLater(() -> snakeEvent.onEvent(die));
        }
    }

    public void clearAll() {
        remove();
        head.remove();
        body.clear();
        Food food;
        if ((food = foodQueue.poll()) != null) {
            food.vanish();
        }
    }

    public List<SnakePart> getHeadAndBody() {
        ArrayList<SnakePart> snakeParts = new ArrayList<>();
        snakeParts.add(head);
        snakeParts.addAll(body);
        return snakeParts;
    }

    public void setSnakeEvent(SnakeEvent snakeEvent) {
        this.snakeEvent = snakeEvent;
    }

    public interface SnakeEvent {
       void onEvent(EventType type);
    }

    public enum EventType {
        DIE(new Data(0));
        private final Data data;

        EventType(Data data) {
            this.data = data;
        }

        public Data getData() {
            return data;
        }
    }
    public static class Data {
        private Snake snake;
        private int score;
        public Data() {
        }
        public Data(int score) {
            this.score = score;
        }

        public int getScore() {
            return score;
        }

        public void setScore(int score) {
            this.score = score;
        }

        public Snake getSnake() {
            return snake;
        }

        public void setSnake(Snake snake) {
            this.snake = snake;
        }
    }
}

SnakeRun

package com.example.snake;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
import javafx.scene.image.Image;
import javafx.scene.layout.*;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

import java.util.Optional;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;

public class SnakeRun extends Application {
    private static final int multiple = 22;

    private static final int width = SnakePart.DEF_LEN * (multiple + 10);

    private static final int height = SnakePart.DEF_LEN * multiple;

    private double windowXOffset = 0;

    private double windowYOffset = 0;

    private final ArrayBlockingQueue<Food> foodQueue = new ArrayBlockingQueue<>(1);

    private MediaPlayer mediaPlayer;

    private AnchorPane pane;

    private Canvas canvas;

    private boolean stop;

    @Override
    public void start(Stage stage) throws Exception {
        pane = new AnchorPane();
        canvas = new Canvas(width, height);
        Snake snake = new Snake(SnakePart.DEF_LEN * 6, SnakePart.DEF_LEN * 6, 5, 10, canvas, foodQueue);
        //监听死亡事件
        snake.setSnakeEvent(type -> alert(type, stage));
        snake.init();
        pane.getChildren().add(canvas);
        pane.setOnKeyPressed(snake::turn);
        pane.setBackground(getBackground());

        //鼠标按下 记录位置
        pane.setOnMousePressed(event -> {
            windowXOffset = event.getSceneX();
            windowYOffset = event.getSceneY();
        });
        //鼠标拖动 移动stage窗口
        pane.setOnMouseDragged(event -> {
            stage.setX(event.getScreenX() - windowXOffset);
            stage.setY(event.getScreenY() - windowYOffset);
        });

        Scene scene = new Scene(pane);
        stage.setScene(scene);
        stage.setResizable(false);
        //stage.setTitle("贪吃蛇");
        //隐藏窗口标题和操作
        stage.initStyle(StageStyle.UNDECORATED);
        //退出事件
        stage.setOnCloseRequest(event -> {
            Platform.exit();
            System.exit(0);
        });
        stage.show();
        //获取窗口焦点
        canvas.requestFocus();
        //初始化并播放音乐
        initMedia();
        //开启食物生成线程
        genFood(snake, canvas);
        //运行蛇
        snake.run();


    }

    private Background getBackground() {
        BackgroundImage bImg = new BackgroundImage(
                new Image(this.getClass().getResourceAsStream("/com/example/fx/bj11.jpg")),
                BackgroundRepeat.NO_REPEAT,
                BackgroundRepeat.NO_REPEAT,
                BackgroundPosition.DEFAULT,
                new BackgroundSize(-1, -1, true, true, false, true));
        return new Background(bImg);
    }

    private void initMedia() {
        Media media = new Media(this.getClass().getResource("/com/example/fx/dizi.mp3").toString());
        mediaPlayer = new MediaPlayer(media);
        mediaPlayer.setCycleCount(Integer.MAX_VALUE);
        mediaPlayer.play();
    }

    private void genFood(Snake snake, Canvas canvas) {
        new Thread(() -> {
            Random random = new Random();
            while (!stop) {
                int x = random.nextInt(multiple - 1);
                int y = random.nextInt(multiple - 1);
                for (SnakePart snakePart : snake.getHeadAndBody()) {
                    if (snakePart.getX() == x && snakePart.getY() == y) {
                        x = random.nextInt(multiple - 1);
                        y = random.nextInt(multiple - 1);
                    }
                }
                try {
                    Food food = new Food(x * SnakePart.DEF_LEN, y * SnakePart.DEF_LEN, canvas.getGraphicsContext2D());
                    foodQueue.put(food);
                    if (!stop) {
                        Platform.runLater(food::show);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }).start();
    }

    private void alert(Snake.EventType eventType, Stage stage) {
        if (eventType == Snake.EventType.DIE) {
            Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
            alert.setTitle("死亡确认");
            alert.setHeaderText("警报,你已经死亡了!!");
            alert.setContentText("当前得分:" + eventType.getData().getScore() + "分");
            ButtonType buttonTypeOne = new ButtonType("重新开始");
            ButtonType buttonTypeTwo = new ButtonType("关闭游戏");
            ButtonType buttonTypeCancel = new ButtonType("取消", ButtonBar.ButtonData.CANCEL_CLOSE);

            alert.getButtonTypes().setAll(buttonTypeOne, buttonTypeTwo, buttonTypeCancel);
            Optional<ButtonType> result = alert.showAndWait();
            if (result.get() == buttonTypeOne) {
                eventType.getData().getSnake().clearAll();
                Snake snake = new Snake(SnakePart.DEF_LEN * 6, SnakePart.DEF_LEN * 6, 5, 10, canvas, foodQueue);
                snake.setSnakeEvent(type -> alert(type, stage));
                pane.setOnKeyPressed(snake::turn);
                snake.init();
                snake.run();
            } else if (result.get() == buttonTypeTwo) {
                stop = true;
                mediaPlayer.stop();
                eventType.getData().getSnake().clearAll();
                stage.close();
            } else {
                alert.close();
            }
        }

    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容