当前位置: 首页 > 后端技术 > Java

周末无聊用Java写一个扫雷游戏

时间:2023-04-02 02:10:40 Java

周末无聊,用Java写了一个扫雷程序。说起来,这应该是我上学的时候写的。写起来会更有趣。毕竟自己实现一个小游戏还是比较好玩的。说实话,扫雷程序里面最核心的东西只是在点击的时候触发更新数据的步骤。Swing已经过时了,但是很好玩,不会过时。不喜勿喷源码地址:https://github.com/Damaer/Gam...说说里面的设计:数据结构设计View和datashouldbeseparatedas尽可能多的点击在使用BFS扫描判断成功或失败时,在这个程序中设计了数据结构。为了方便,使用全局数据类Data来维护整个游戏的数据,直接设置为静态变量,即一次只能运行一个游戏窗口。否则会出现数据安全问题。(只是为了方便)有以下数据(部分代码):publicclassData{//游戏状态publicstaticStatusstatus=Status.LOADING;//雷区大小publicstaticintsize=16;//地雷数量publicstaticintnumOfMine=0;//表示是否有地雷,1:有,0没有publicstaticint[][]maps=null;//是否访问过publicstaticboolean[][]visited=null;//周围的地雷publicstaticint[][]nums=null;//是否标记publicstaticboolean[][]flags=null;//最后访问的块坐标publicstaticPointlastVisitedPoint=null;//困难模式privatestaticDifficultModeEnummode;...}需要维护的数据如下:游戏状态:是否开始、结束、成功、失败等。模式:简单、中等或困难,会影响自动生成地雷的数量。*16小方块地雷数量:与模式选择有关,是一个随机数,用于标识每个方块是否有地雷:最基础的数据,生成后需要同步更新此数据,以表明每个方块是否有地雷扫过:默认不扫每个方块周围的地雷数量:生成时同步计算结果。我不想每次点击都计算它。毕竟是不会更新的数据。一劳永逸,标记块是否被标记:我们在扫雷的时候用一个小旗子来标记块。表示这是一道雷。标记完所有地雷后,最后一次成功访问的方块坐标:这个可以不记录,但是为了展示爆炸效果,和其他地雷不一样,所以分别记录下视图和数据,尽量按照可能的一个原则是视图与数据或数据更改分离,以便于维护。我们知道Java是使用Swing来绘制图形界面的。这东西真的很难画。视图写起来比较复杂,但什么也画不出来。视图和数据的分离也是几乎所有框架的一个优秀特性。主要是方便维修。如果视图和数据混在一起,更新数据,还要操作视图,会很乱。(当然,我写的是粗略的版本,只是简单的区分。)在这个扫雷程序中,基本都是点击事件,触发数据变化。数据变化后调用视图刷新,视图渲染逻辑和数据变化逻辑分开维护。.每个小方块都添加了一个点击事件,Data.visit(x,y)是数据刷新,repaintBlocks()是刷新视图,具体代码就不放了,感兴趣的可以去Github上查看源码:newMouseListener(){@OverridepublicvoidmouseClicked(MouseEvente){if(Data.status==Status.GOING){intc=e.getButton();//获取按下的鼠标按钮Blockblock=(Block)e.getComponent();intx=block.getPoint_x();inty=block.getPoint_y();如果(c==MouseEvent.BUTTON1){Data.visit(x,y);}elseif(c==MouseEvent.BUTTON3){//推断是鼠标右键被按下if(!Data.visited[x][y]){Data.flags[x][y]=!Data.flags[x][y];}}}重新绘制块();}}对不起最重要的一点是,每个块中都有一个后台url没有被提取出来。这是变化的数据,不应该放在视图中:publicclassBlockextendsJPanel{privateintpoint_x;私人intpoint_y;privateStringbackgroundPath=ImgPath.DEFAULT;publicBlock(intx,inty){this.point_x=x;this.point_y=y;setBorder(BorderFactory.createEtchedBorder());}}重置块背景,需要居中,重绘,重写voidpaintComponent(Graphicsg)方法就够了:@OverrideprotectedvoidpaintComponent(Graphicsg){refreshBackground();网址url=getClass().getClassLoader().getResource(backgroundPath);ImageIconicon=newImageIcon(url);如果(backgroundPath.equals(ImgPath.DEFAULT)||backgroundPath.equals(ImgPath.FLAG)||backgroundPath.equals(String.format(ImgPath.NUM,0))){g.drawImage(icon.getImage(),0,0,getWidth(),getHeight(),这个);}else{intx=(int)(getWidth()*0.1);inty=(int)(getHeight()*0.15);G。画图e(icon.getImage(),x,y,getWidth()-2*x,getHeight()-2*y,这个);}}BFS扫描BFS,又称广度优先搜索,是扫雷的核心知识点,就是当你点击的时候,如果当前块为空,那么就会触发扫描周围的块,如果周围有块也为空,它会继续递归。我用的是广度优先搜索,意思是先放入队列,取出,然后判断是否为空,然后将周围的匹配块加入其中,逐一处理。重点,这就是为什么我们需要一个队列,我们??需要一个队列来保存遍历的顺序。publicstaticvoidvisit(intx,inty){lastVisitedPoint.x=x;lastVisitedPoint.y=y;if(maps[x][y]==1){status=Status.FAILED;//游戏绑定,暴露所有的雷}else{//点击的不是雷Queuepoints=newLinkedList<>();points.add(新点(x,y));while(!points.isEmpty()){Pointpoint=points.poll();已访问[point.x][point.y]=true;如果(nums[point.x][point.y]==0){addToVisited(points,point.x,point.y);}}}}publicstaticvoidaddToVisited(Queuepoints,inti,intj){intx=i-1;while(x<=i+1){if(x>=0&&x=0&&y<大小){如果(!(x==i&&j==y)){//没有访问过,也不是我的if(!visited[x][y]&&maps[x][y]==0){points.add(new点(x,y));}}}y++;}}x++;}}值得注意的是,如果周围的点周围没有地雷,它会继续扩张,但只要周围有地雷,它就会停止扩张,只会显示数字来判断成功或失败。地雷挖完就会失败,同时所有的地雷都会暴露出来。为了显示我们当前挖到的点,加上爆炸效果,我们记录了上一步的点,刷新视图后,弹窗提示:如果判断成功,需要遍历所有地雷一次,判断是否有标记。这是我简单想到的规则。当雷区全部被挖空,成功也是有可能的。总结一下,扫雷是个简单的游戏,无聊的时候可以试试,但是Java的Swing实在是太难用了,想找一个数据驱动的视图修改框架,但是好像没有,所以干脆实施它。其实我大部分时间都在找图标和测试UI,核心代码不多。这里推荐一下图标网站:https://www.iconfont.cn/,就算是没有任何技术含量的扫雷,写出来也挺有意思的。【作者简介】:公众号【秦淮杂货铺】作者秦淮,个人网站:http://aphysia.cn,技术之路非一蹴而就,山高水长江河漫漫,纵然缓慢,也不会停下脚步。剑指全题OfferPDF开源编程笔记