实时

您的位置:首页>资讯 >

基于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,标记旗帜;如果已有旗帜标记,值不变,替换为问号标记;如果已有问号标记,值加上1,去除格子上的标记。左键点击有标记的格子时,不管是哪种标记,值统统加上1,去除标记。

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

// 创建具有可观察特性的整数变量rest = new ReadOnlyIntegerWrapper(REST_FLAG);// 添加监听器, 在变量值变化时执行相应的操作, 下同ChangeListener 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 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);

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

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

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

关键词:

推荐阅读
相信阅读过上期文章,动手能力强的朋友们已经自己跑出来界面了。所以这

2023-07-07 17:42:06

新一代全球个护科技品牌TYMO近日宣布完成数千万人民币A轮融资。

2023-07-07 17:11:41

为深入了解新形势下济南市重大决策社会稳定风险评估行业发展现状,听取

2023-07-07 16:56:13

步入数字经济时代,算力的重要性日益凸显。我国“东数西算”工程启动以

2023-07-07 16:21:55

据俄罗斯卫星通讯社7日报道,6日,俄罗斯总统办公厅第一副主任基里延科

2023-07-07 16:01:00

1、在86年前的今天2、86年前的今天永不敢忘3、以上就是关于【在86年前

2023-07-07 15:10:09

一:要找到一块平坦的滑行场地这个不用多说大家应该都知道,场地如果不

2023-07-07 14:57:34

各位看官朋友们大家好,这期小编给大家推荐的新能源车型是梅赛德斯奔驰

2023-07-07 14:10:22

1、惠普DeskjetInkAdvantage2020hc超级惠省打印机,具备黑色和彩色两种

2023-07-07 13:13:10

2023年7月深圳福田区选聘优秀教师现场资格审查入围现场资格初审的考生

2023-07-07 12:43:18

既然没有“一下子”的奇迹,那就打破你自己的记录,不断刷新自己与苦难

2023-07-07 12:08:28

湖人官宣签下6人后就还差三人没有官宣了,丹吉洛-拉塞尔的两年3700万,

2023-07-07 11:36:29

世界锻造者用自己的计划让主世界的正义联盟进入了他建造的多元宇宙,

2023-07-07 11:07:48

中大型企业的信息化建设已经成为了当前企业的主流趋势。随着日益增长的

2023-07-07 10:47:22

美国商务部7月6日公布的数据显示,受商品进口金额下降影响,5月该国商

2023-07-07 10:18:15

打通15 2公里的隧道,为何用了7年多?

2023-07-07 10:00:31

河南日报客户端记者孔学姣“你爱我我爱你,蜜雪冰城甜蜜蜜。”有网友调

2023-07-07 09:32:40

来为大家解答以下的问题,渺行无极扬帆但信风是什么意思,ldquo浩渺行

2023-07-07 09:01:59

张掖七彩丹霞景区:暑期旅游单日接待游客2 17万人次  每日甘肃网7月6

2023-07-07 08:14:29

7月6日,工人在位于慈溪市的宁波宁达电器有限公司生产电风扇。今年以来

2023-07-07 07:33:46