爱美容
当前位置: 首页 美容百科

javaswing扫雷游戏(无聊的周末用Java写个扫雷小游戏)

时间:2023-05-26 作者: 小编 阅读量: 1 栏目名: 美容百科

周末无聊,用Java写了一个扫雷程序,说起来,这个应该是在学校的时候,写会比较好玩,毕竟自己实现一个小游戏,还是比较好玩的。我们知道Java里面是用Swing来画图形界面,这个东西确实难画,视图写得比较复杂但是画不出什么东西。视图与数据分开,也是几乎所有框架的优秀特点,主要是方便维护,如果视图和数据糅合在一起,更新数据,还要操作视图,那就会比较乱。

周末无聊,用Java写了一个扫雷程序,说起来,这个应该是在学校的时候,写会比较好玩,毕竟自己实现一个小游戏,还是比较好玩的。说实话,扫雷程序里面核心的东西,只有点击的时候,去触发更新数据这一步。

Swing 是过时了,但是好玩不会过时,不喜勿喷

源码的地址:https://Github.com/Damaer/Game/tree/main/SweepMine

下面讲讲里面的设计:

  • 数据结构设计
  • 视图和数据尽可能分开
  • 点击时候使用BFS扫描
  • 判断成功失败
数据结构设计

在这个程序里面,为了方便,使用了全局的数据类Data类来维护整个游戏的数据,直接设置为静态变量,也就是一次只能有一个游戏窗口运行,否则会有数据安全问题。(仅仅是为了方便)

有以下的数据(部分代码):

public class Data {// 游戏状态public static Status status = Status.LOADING;// 雷区大小public static int size = 16;// 雷的数量public static int numOfMine = 0;// 表示是否有雷,1:有,0没有public static int[][] maps = null;// 是否被访问public static boolean[][] visited = null;// 周边雷的数量public static int[][] nums = null;// 是否被标记public static boolean[][] flags = null;// 上次被访问的块坐标public static Point lastVisitedPoint = null;// 困难模式private static DifficultModeEnum mode;...}

需要维护的数据如下:

  • 游戏状态:是否开始,结束,成功,失败等等
  • 模式:简单,中等或者困难,这个会影响自动生成的雷的数量
  • 雷区的大小:16*16的小方块
  • 雷的数量:与模式选择有关,是个随机数
  • 标识每个方块是否有雷:最基础的数据,生成之后需要同步更新这个数据
  • 标识每个方块是否被扫过:默认没有扫过
  • 每个方块周边类雷的数量:生成的时候同步计算该结果,不想每次点击后再计算,毕竟是个不会更新的数据,一劳永逸
  • 标识方块是否被标记:扫雷的时候我们使用小旗子标记方块,表示这里是雷,标识完所有的雷的时候,成功
  • 上次访问的方块坐标:这个其实可以不记录,但是为了表示爆炸效果,与其他的雷展示不一样,故而记录下来
视图与数据分开

尽量遵循一个原则,视图与数据或者数据变更分开,方便维护。我们知道Java里面是用Swing来画图形界面,这个东西确实难画,视图写得比较复杂但是画不出什么东西。

视图与数据分开,也是几乎所有框架的优秀特点,主要是方便维护,如果视图和数据糅合在一起,更新数据,还要操作视图,那就会比较乱。(当然我写的是粗糙版本,只是简单区分了一下)

在这个扫雷程序里面基本都是点击事件,触发了数据变更,数据变更后,调用视图刷新,视图渲染的逻辑与数据变更的逻辑分开维护。

每个小方块都添加了点击事件,Data.visit(x, y)是数据刷新,repaintBlocks()是刷新视图,具体的代码就不放了,有兴趣可以Github看看源代码:

new MouseListener() {@Overridepublic void mouseClicked(MouseEvent e) {if (Data.status == Status.GOING) {int c = e.getButton(); // 得到按下的鼠标键Block block = (Block) e.getComponent();int x = block.getPoint_x();int y = block.getPoint_y();if (c == MouseEvent.BUTTON1) {Data.visit(x, y);} else if (c == MouseEvent.BUTTON3) {// 推断是鼠标右键按下if (!Data.visited[x][y]) {Data.flags[x][y] = !Data.flags[x][y];}}}repaintBlocks();}}

这里很遗憾的一点是每个方块里面还有一个背景的``url`没有抽取出来,这个是变化的数据,不应该放在视图里面:

public class Block extends JPanel {private int point_x;private int point_y;private String backgroundPath = ImgPath.DEFAULT;public Block(int x, int y) {this.point_x = x;this.point_y = y;setBorder(BorderFactory.createEtchedBorder());}}

重新设置方块背景,需要居中处理,重新绘制,重写void paintComponent(Graphics g)方法即可:

@Overrideprotected void paintComponent(Graphics g) {refreshBackground();URL url = getClass().getClassLoader().getResource(backgroundPath);ImageIcon icon = new ImageIcon(url);if (backgroundPath.equals(ImgPath.DEFAULT) || backgroundPath.equals(ImgPath.FLAG)|| backgroundPath.equals(String.format(ImgPath.NUM, 0))) {g.drawImage(icon.getImage(), 0, 0, getWidth(), getHeight(), this);} else {int x = (int) (getWidth() * 0.1);int y = (int) (getHeight() * 0.15);g.drawImage(icon.getImage(), x, y, getWidth() - 2 * x, getHeight() - 2 * y, this);}}

BFS扫描

BFS,也称为广度优先搜索,这算是扫雷里面的核心知识点,也就是点击的时候,如果当前方块是空的,那么就会触发扫描周边的方块,同时周边方块如果也是空的,会继续递归下去,我用了广度优先搜索,也就是先将它们放到队列里面,取出来,再判断是否为空,再将周边符合的方块添加进去,进行一一处理。

广度优先搜索在这里不展开,其本质是优先搜索与其直接关联的数据,也就是方块周围的点,这也是为什么需要队列的原因,我们需要队列来保存遍历的顺序。

public static void visit(int x, int y) {lastVisitedPoint.x = x;lastVisitedPoint.y = y;if (maps[x][y] == 1) {status = Status.FAILED;// 游戏结束,暴露所有的雷} else {// 点击的不是雷Queue<Point> points = new LinkedList<>();points.add(new Point(x, y));while (!points.isEmpty()) {Point point = points.poll();visited[point.x][point.y] = true;if (nums[point.x][point.y] == 0) {addToVisited(points, point.x, point.y);}}}}public static void addToVisited(Queue<Point> points, int i, int j) {int x = i - 1;while (x <= i1) {if (x >= 0 && x < size) {int y = j - 1;while (y <= j1) {if (y >= 0 && y < size) {if (!(x == i && j == y)) {// 没访问过且不是雷if (!visited[x][y] && maps[x][y] == 0) {points.add(new Point(x, y));}}}y;}}x;}}

值得注意的是,周边的点,如果它的周边没有雷,那么会继续拓展,但是只要周边有雷,就会停止拓展,只会显示数字。

判断成功失败

当挖到雷的时候,就失败了,同时会将所有的雷暴露出来,为了展示我们当前挖到的点,有爆炸效果,我们记录了上一步操作的点,在刷新视图后,弹窗提示:

判断成功则需要将所有的雷遍历一次,判断是否被标记出来,这是我简单想的规则,忘记了扫雷是不是这样了,或者可以实现将其他所有非雷区都挖空的时候,成功,也是可以的。

总结

扫雷,一个简单的游戏,无聊的时候可以尝试一下,但是Java 的Swing真的难用,想找一个数据驱动视图修改的框架,但是貌似没有,那就简单实现一下。其实大部分时间都在找图标,测试UI,核心的代码并没有多少。

在这里推荐一下icon网站:https://www.iconfont.cn/,即使是没有什么技术含量的扫雷,写一下还是挺有趣的。

【作者简介】:秦怀,公众号【秦怀杂货店】作者,个人网站:http://aphysia.cn,技术之路不在一时,山高水长,纵使缓慢,驰而不息。

剑指Offer全部题解PDF

开源编程笔记

    推荐阅读
  • 新手刷酸产品推荐干皮(敲详细刷酸教程)

    敲详细刷酸教程刷酸也有一段时间了今天做了个小小的总结,包含了各个阶段不同的刷酸方法~—先来说一下怎么去选酸新手——维A酸乳膏进阶——水杨酸棉片(0.5%浓度)老手——果酸(30%浓度)—如果之前没刷过酸的话,记得现。

  • 刀锋鞋适合什么人穿(刀锋鞋的原理)

    刀锋鞋适合什么人穿刀锋跑鞋当然适合跑步运动的青年人,不适合体重过大人群,刀锋跑鞋是2013年6月24日在美国波特兰发布一款结构减震跑鞋。鞋底搭载了16个由高科技聚合物制成的叶片,为跑者提供最高的纵向能量反馈。保护膝盖,减少运动消耗,延长运动时间,提高运动效率。

  • 石家庄紧急寻人通告(河北多地最新公告石家庄)

    石家庄紧急寻人通告行程轨迹公布!定州紧急提醒广大市民:2月28日0时至24时,天津市新增6例确诊病例,其中滨海新区4例、东丽区2例。截至目前,本轮疫情累计报告19例阳性感染者,均在定点医院治疗。为保障广大市民身体健康与生命

  • 2.7霸道加92号油可以吗 丰田霸道3.5可以加92号油吗

    有毒苯含量不得超过1%。是的,这款搭载奥迪新一代共轨直喷柴油发动机的A6L2.7TDI绝对值得好奇了解甚至驾驶。经过几天的试驾,这款奥迪A6L2.7TDI的总里程约为960公里,油表显示还能开100公里。从这几天试驾的操控性表现来看,奥迪A6L2.7TDI作为一款豪华行政轿车,其操控性和其他奥迪A6L一样稳健舒适。轮毂方面,A6L2.7TDI配备了与2.4款相同的7辐17英寸轮毂,轮胎尺寸完全相同。

  • 蒸2个鸡蛋要放多少水(这里有答案)

    蒸2个鸡蛋要放多少水?下面更多详细答案一起来看看吧!准备好所有材料,把两个鸡蛋放在碗中打散打匀。在鸡蛋液中加入156ml的饮用水,用筷子继续打匀。打匀后拿筛子把泡沫全部过滤掉,一定要过滤干净。盖上保鲜薄膜,防止水滴进去鸡蛋羹不平。小火蒸12分钟后,把火关掉,继续焖5分钟。五分钟后拿出来后在鸡蛋液上倒上酱油和麻油就可以开动了。

  • 简笔画少女古装美人(人物简笔画女孩可爱古装)

    少女古装美人简笔画你会画吗?儿童画第1页漫画图片简笔美女漫画人物简笔画少女古装古装漫画人物,下面来看看有没有你喜欢的简笔画少女古装美人?接下来我们一起去欣赏以下简笔画作品吧!

  • 如何让蒸蛋又嫩又滑好吃不翻车呢(蒸蛋又嫩又滑做法)

    接下来我们就一起去了解一下吧!如何让蒸蛋又嫩又滑好吃不翻车呢将鸡蛋打入碗里打散,一定要打得很散。加入温开水,边加水边搅拌,多搅拌几分钟。加入适量的盐,继续打散,用小勺撇去上面的一层泡沫不要。碗上盖上盖子,也可以用保鲜膜,蒸锅里加好水烧开,把蒸碗放入蒸锅。盖好盖子,蒸12分钟左右就好了。最后起锅后可加点葱花,芝麻油。

  • 牙膏怎么选择才是好的(如何选择牙膏)

    牙膏怎么选择才是好的试挤,质量良好的牙膏应能自然地挤出,膏体比较细腻光滑,并非正常的圆条状。在阳光反射下看细腻、光滑、洁净、无杂质和发黑现象,而质量低劣的牙膏发硬挤不出,或稍一挤膏体便大量涌出。试尝,将牙膏挤出少许试尝,质量好的牙膏香味纯正,无刺激味道。如果口感粗糙,有像沙子一样的颗粒滞留在嘴里,大多是含粗糙磨擦剂的牙膏,建议立即停用。对症选用药物牙膏,根据防蛀功效选用含氟牙膏。

  • 苹果充电器怎么保护(苹果手机如何充电才保护电池)

    苹果充电器怎么保护由于iPhone使用的是锂聚合物电池,并不存在记忆效应,因此并不需要等到手机彻底没电或者是低电量之后再充电,也就是说随时都可以充电。由于手机内还有一个充放电的计量芯片,每个月只需要完成一次完整的充放电就能够将电池进行校准。建议在充电的时候使用质量合格的充电器以及数据线,由于目前iPhone手机已经支持PD的快充协议,因此使用支持PD的快充头是能够支持快充。

  • 汽车电瓶什么情况下才换(汽车电瓶换完了)

    与电动转向泵通信才是重点发动机电脑自身可是重要部件,它要坏了,可能就打不着车了,所以咱们检查电动转向泵,这个标致3008,电动转向泵油是不缺的,通过诊断电脑我们去查找泵的电路图,搞清楚泵的供电和接地以及信号线。至此真相大明,紧固螺丝,清除故障码,试车正常。我接着说,重点在于你没换电瓶之前,都是好的,她说是的是的,这个标致3008外形时尚,平时开着也省心,我很喜欢这车,但就是换了电瓶之后就这不好,那不好了。