基于JavaFX的扫雷游戏实现(三)——交互逻辑

这篇具有很好参考价值的文章主要介绍了基于JavaFX的扫雷游戏实现(三)——交互逻辑。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

  相信阅读过上期文章,动手能力强的朋友们已经自己跑出来界面了。所以这期我要讲的是交互部分,也就是对于鼠标点击事件的响应,包括计时计数对点击事件以及一些状态量的影响。

  回忆下第一期介绍的扫雷规则和操作,游戏从开局到结束可能会涉及到哪些情况呢?我认为比较重要的就是明确什么情况下游戏已经结束,结束代表的是胜利还是失败。对此我定义了一个游戏状态量,他有位置、胜利和失败三种可选值,如下:

// 游戏状态相关 [1:获胜, 0:未知, -1:失败]
public static byte WIN = 1;
public static byte UNSURE = 0;
public static byte LOSS = -1;
public static byte STATE = UNSURE;

  很显然游戏只要还未结束,就应该保持在未知状态。那么哪些情况会影响到状态量的取值,就需要我们逐个分析了。

  根据规则,当我们把除地雷以外的所有格子均点开后便取得胜利,所以右键点击并不会对游戏状态造成影响。那我们仅需在每次左键点击处理中进行格子数统计,符合要求就修改游戏状态为胜利,点击到地雷便修改为失败。另外每次点击都需要更新相关格子的显示,所以这两项任务可以放在一起进行,做法如下:

// 更新点击过的数据
mineSweeper.clickCell(row, column);

  执行完后就对游戏状态进行判断,如果没有点击到地雷,执行 STATE == UNSURE 部分:

if (STATE == UNSURE) {
    // 统计非雷格子已点开数目
    int count = 0;
    for (int i = 0; i < GAME.height; ++i) {
        for (int j = 0; j < GAME.width; ++j) {
            if (map[i][j] > BOUND) {
                Button btn = (Button) buttons.get(i * GAME.width + j);
                count += 1;
                int value = map[i][j] - 100;
                if (value != BLANK) {
                    // 消除空白填充
                    btn.setPadding(new Insets(0.0));
                    // 设置粗体和字体颜色
                    btn.setFont(Font.font("Arial", FontWeight.BOLD, GAME.numSize));
                    btn.setTextFill(NUMS[value - 1]);
                    btn.setText(value + "");
                }
                btn.setStyle("-fx-border-color: #737373; -fx-opacity: 1; -fx-background-color: #ffffff");
                btn.setDisable(true);
            }
        }
    }
    // 判断全部非雷格子是否全部点开
    if (count + GAME.bomb == GAME.width * GAME.height) {
        STATE = WIN;
    }
}

  否则执行 STATE == LOSS 部分:

if (STATE == LOSS) {
    // 游戏失败, 显示所有地雷位置
    for (int i = 0; i < GAME.height; ++i) {
        for (int j = 0; j < GAME.width; ++j) {
            if (map[i][j] == BOMB) {
                Button btn = (Button) buttons.get(i * GAME.width + j);
                btn.setStyle("-fx-background-color:#ffffff; -fx-background-size: contain; -fx-background-image: url(" + UNEXPLODED_IMG + ")");
            }
        }
    }
    button.setStyle("-fx-background-color:#ffffff; -fx-background-size: contain; -fx-background-image: url(" + EXPLODED_IMG + ")");
}

  看上去似乎所有任务都完成了,真的是这样吗?别忘了还有计时功能,时间超出指定范围也可以认为是游戏失败。上期说过计时计数这块有自定义控件,这期它依旧不是主角,但是我会大致说明下它的工作方式。如果你还记得游戏界面那两个黑框框是GridPane布局的话,显示出的数字就是其中的控件外观。我使用的是三位数,也就是说每个布局中都含有三个数字自定义控件,根据数值不同排列组合表示不同整数。

  首先来讲计时,这里JavaFX提供的有时间轴类,直接拿来用非常方便。我们可以设置事件触发的间隔,对应到扫雷里自然是每秒触发一次。事件中要做的就是判断游戏状态和是否超时,下面给出代码以供参考。

  涉及到的量:

// 时间计数和超时范围
public static int TIMER = 0;
public static int OVERTIME = 999;
// 计时器
public static Timeline TIMELINE = null;

  计时事件:

TIMELINE = new Timeline(
        new KeyFrame(Duration.seconds(1), event -> {
            TIMER += 1;
            // 超时自动判负
            if (TIMER >= OVERTIME) {
                STATE = LOSS;
            }
            // 游戏胜负已确定
            if (STATE != UNSURE) {
                String path = WIN_IMG;
                TIMELINE.stop();
                if (STATE == LOSS) {
                    path = LOSS_IMG;
                } else {
                    // 自定义模式不计入成绩
                    if (GAME != GameEnum.CUSTOM) {
                        Platform.runLater(() -> showDialog());
                    }
                }
                reset.setStyle("-fx-background-size: contain; -fx-background-image: url(" + path + ")");
            }
            ledTime[0].switchSkin(TIMER / 100);
            ledTime[1].switchSkin(TIMER % 100 / 10);
            ledTime[2].switchSkin(TIMER % 10);
        })
);

  接下来是计数功能,数字显示原理同上,主要是交互。这个数字表示的是游戏中剩余可用标记数 REST_FLAG,它的值通过左右键点击改变。它的改变规则具体如下:

  1. 该数值初始大小等于地雷数目。
  2. 右键点击未知格子时,如果先前没有标记,那么值减去1,标记旗帜;如果已有旗帜标记,值不变,替换为问号标记;如果已有问号标记,值加上1,去除格子上的标记。
  3. 左键点击有标记的格子时,不管是哪种标记,值统统加上1,去除标记。

  接下来需要考虑如何监听 REST_FLAG 值的变化,通过查阅资料,我找到了一种方案 ReadOnlyIntegerWrapper。该类提供了一个方便的类来定义只读属性。它创建两个同步的属性。一个属性是只读的,可以传递给外部用户。另一个属性是可读写的,只能在内部使用。最重要的是可以对它设置监听器,在值发生变化时执行一些操作,实现如下:

// 创建具有可观察特性的整数变量
rest = new ReadOnlyIntegerWrapper(REST_FLAG);
// 添加监听器, 在变量值变化时执行相应的操作, 下同
ChangeListener<? super Number> restListener = (observable, oldValue, newValue) -> {
    // 在变量值变化时执行相应的操作
    ledMark[0].switchSkin(REST_FLAG / 100);
    ledMark[1].switchSkin(REST_FLAG % 100 / 10);
    ledMark[2].switchSkin(REST_FLAG % 10);
};
// 将监听器绑定到rest属性
rest.addListener(restListener);

  这些工作完成后,我们再来考虑一个有关计时的问题。什么时机开始计时较为合适呢?是进入游戏界面,还是第一次点击格子?我认为后者更符合要求。当然这个全看个人设计,如果采用后者的方案的话,也需要设置对应的值来监听,比如下面这种:

// 游戏是否开局, 即格子是否被点击过 [1:是, 0:否]
public static int YES = 1;
public static int NO = 0;
public static int CLICKED = NO;

  然后把上边提到的监听事件与之结合:

clicked = new ReadOnlyIntegerWrapper(CLICKED);
ChangeListener<? super Number> clickListener = (observable, oldValue, newValue) -> {
    // 已经被点击, 开始计时
    TIMER = 0;
    // TODO 这里放入计时监听事件
    TIMELINE.setCycleCount(Animation.INDEFINITE);
    TIMELINE.play();
};
clicked.addListener(clickListener);

   值发生变化后需要手动调用set方法触发监听:

// 判断游戏是否开局
if (CLICKED == NO) {
    CLICKED = YES;
    clicked.set(CLICKED);
}

// 触发监听, 修改剩余地雷数显示
rest.set(REST_FLAG);

  截止到这里,有关游戏部分就只剩下排行榜功能未介绍了。至于鸽了好几期都没说的自定义控件,因为我觉得它的实现并不重要,了解它的作用一样能理解前边的内容,所以就放在最后一期再说吧。

——————————————我———是———分———割———线—————————————

  我居然更到第三期了哎,一周之内呀!太勤快了吧!不行,最多再更两期,我要报仇雪恨般地拖更,拖拖拖拖拖拖拖一拖到明年,大好时光怎么能天天用来码文呢?我要打电动去啦,阿伟也拦不住,我说的!文章来源地址https://www.toymoban.com/news/detail-529813.html

到了这里,关于基于JavaFX的扫雷游戏实现(三)——交互逻辑的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C语言】实现扫雷游戏

    详细介绍扫雷游戏的思路和实现过程,并用随机数实现埋雷。 • 使用控制台实现经典的扫雷游戏 • 游戏可以通过菜单实现继续玩或者退出游戏 • 扫雷的棋盘是9*9的格⼦ • 默认随机布置10个雷 • 可以排查雷 ◦ 如果位置不是雷,就显示周围有几个雷 ◦ 如果位置是雷,就炸

    2024年02月04日
    浏览(45)
  • 小游戏扫雷实现教学(详解)

    目录  【前言】 一、模块化程序设计(多文件编程)介绍 1.概述 2.传统编程的方式 3.模块化程序设计的方法 二、扫雷代码设计思路 三、扫雷代码设计 1.创建菜单函数  2.实现9x9扫雷 3.初始化棋盘  4.打印棋盘  5.随机布置雷的位置 6.排查雷的信息  7.回到步骤1,重新选择进入

    2024年02月12日
    浏览(36)
  • 扫雷游戏的实现(C语言)

    对于扫雷游戏,大家应该都很熟悉吧,下面让我们来看看它是如何实现的。 目录 一、游戏规则及设计思想 二、各功能的代码实现 1.创建菜单 2、主函数的实现  3、创建棋盘并初始化 4、打印棋盘  5、布置雷的位置 (埋雷) 6、排查雷   三、代码汇总 1、game.h文件 2、game.c文

    2024年02月03日
    浏览(38)
  • “纯C”实现——扫雷游戏(递归实现展开功能)

    📺游戏动画演示 🚀扫雷实现思路 🚀棋盘实现 🚀布置雷实现 🚀玩家扫雷实现 🚀带展开功能的扫雷 🚀小结语 🚗text.c文件 🚗game.h文件 🚗game.c文件 游戏实现完成的模样: 实现扫雷游戏的前提是要知道: 扫雷游戏的玩法 (会玩的跳过这步) 扫雷游戏也就是排雷,让玩家点

    2024年02月03日
    浏览(47)
  • 探秘C语言扫雷游戏实现技巧

    本篇博客会讲解,如何使用C语言实现扫雷小游戏。 使用2个二维数组mine和show,分别来存储雷的位置信息和排查出来的雷的信息,前者隐藏,后者展示给玩家。假设盘面大小是9×9,这2个二维数组都要开大一圈,也就是大小是11×11,这是为了更加方便的数边角上雷的个数,防止

    2024年02月10日
    浏览(47)
  • 【C语言】扫雷游戏完整代码实现

    目录 1.game.h 2.game.c 3.progress.c 4.运行结果

    2024年02月21日
    浏览(45)
  • C语言实现简单的扫雷游戏

    目录 1 - test.c 2 - game.c 3 - game.h 代码里的注释感觉已经很清楚啦,就不多讲解啦 感谢各位大佬的支持!!!

    2024年01月22日
    浏览(49)
  • C语言之扫雷游戏实现篇

    目录 主函数test.c 菜单函数 选择循环 扫雷游戏实现分析 整体思路  问题1 问题2  问题3 问题4  游戏函数(函数调用)  创建游戏盘数组mine 创建游戏盘数组show 初始化游戏盘数组InitBoard 展示游戏盘DisplayBoard 游戏盘置雷SetMine 游戏盘排雷FindMine test.c总代码 头文件函数声明game

    2024年02月11日
    浏览(38)
  • 详解Java实现2048小游戏(JavaFX,完整源码+注释说明)

    刚刚完成的一个小游戏,写文章记录一下,如果有任何错误或者可以改进的代码请提出 另一方面也是方便自己几个月或几年后忘记时,来这里翻一翻回顾思路 目录 基本界面: 类的组织: _CardPane: _CardMatrixPane: _CardColor: _GameMenuBar: _2048Demo: 基本思路: 卡片: 卡片矩阵:

    2024年02月03日
    浏览(55)
  • C语言:轻松实现扫雷小游戏

    目录 一、前言 二、扫雷步骤 1.创建项目 2.设计整体框架 1.定义数组长度和雷的个数 2.game函数功能 三、头文件game.h代码实现  四、测试文件test.c代码实现 五、game函数功能实现 1.初始化棋盘 2.打印棋盘  3.布置雷 4. 获得雷的个数  5.排查雷 6.game函数实现代码 六、总结 大家好

    2024年02月03日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包