Sweep
一:团队组成
团队名称:Sweep
团队成员介绍:陈玉婷,彭佳妮,罗梅丽
任务分配:
陈玉婷:文件管理,及扫雷游戏算法实现,画UML图
彭佳妮:GUI登陆界面设置,用户密码验证及其监听,写博客
罗梅丽:GUI登陆界面设置,写博客
成员 | 博客链接 |
陈玉婷(组长) | https://www.cnblogs.com/chenyutin/p/10279348.html
|
彭佳妮 | https://www.cnblogs.com/pengjiani/p/10280987.html |
罗梅丽 | https://www.cnblogs.com/luomeili/p/10281816.html |
二:项目Git地址及提交记录
Git地址
https://gitee.com/pengjiani/mine_clearance
提交记录
三:项目简介
游戏介绍:
扫雷游戏介绍:
《扫雷》是一款大众类的益智小游戏。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。
游戏设定:
游戏区包括雷区,确定大小的矩形雷区中随机布置一定数量的地雷(初级为9*9个方块9个雷,中级为14*14个方块14个雷,高级为16*16个方块16个雷,玩家需要尽快找出雷区中的所有不是地雷的方块,而不许踩到地雷。
项目功能架构图与主要功能流程图
功能流程图
功能构架图
组织形式
项目运行演示
优秀设计部分
可以保存游戏进度,增加游戏的灵活性和用户体验
保存游戏进度
再次登陆时进入上一次游戏
项目关键代码
界面显示模块
//登陆界面设置 setSize(496, 557); setLayout(new GridLayout(2, 1)); // JMenuBar bar=new JMenuBar(); HomePanel panelN = new HomePanel("2018-01-10_12-55-41.gif"); HomePanel panelS = new HomePanel("ApplicationFrameHost_2018-01-10_13-37-17.png"); panelN.setLayout(new FlowLayout()); JButton button=new JButton(); button.setIcon(new ImageIcon("ApplicationFrameHost_2018-01-10_13-39-56.png")); //button.setOpaque(false); //设置控件透明 button.setBorder(null); //设置边框 button.setContentAreaFilled(false); //设置控件透明 button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { System.exit(0); } }); //键盘监听 KeyListener key_Listener = new KeyListener() { public void keyTyped(KeyEvent e) {} public void keyReleased(KeyEvent e){} public void keyPressed(KeyEvent e){ if(e.getKeyChar() == KeyEvent.VK_ENTER ) { String name = usenameField.getText(); String word = new String(passwordField.getPassword()); if(judge(name,word) == 0) JOptionPane.showMessageDialog(null, "用户名不存在!"); else if(judge(name,word) == -1) JOptionPane.showMessageDialog(null, "密码错误!"); else { Selection selection=new Selection(file); MFrame.this.dispose(); } try { file.setFileName(usenameField.getText()); // Selection selection=new Selection(file); } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } } }; passwordField.addKeyListener(key_Listener);
算法模块
我们的扫雷由n x m 的方格区域构成,当我们点击其中一个方块的时候,如果它不是雷,它就应当以数字的形式表示出在它外圈的一圈内包含有多少个雷,这个数字的范围是1到8,在它不是雷的前提下,如果它周围也没有雷的话,那我们点击的方块在下一秒应当展开当前的一片无雷的区域,以数字作为边界,该边界指出外圈的雷的数目,我们的做法是,以二维数组的形式定义一片区域,每二维数组上的每一个数字映射着我们雷区的每一块方格,然后就是该二维数组的数字的范围,我们取整型数字的范围为0到9,用9来表示雷,用0到8来表示外围雷的数目,我们这里的0在 后是不用来显示在方块上的,我们判断到方块下面的数字是0的话我们会让雷区展开一片空白区域。
也就是说,我们刚开始初始化了一个二维数组map,来映射一片雷区,其中的整型数字范围为0到9,用9来表示雷。
所以刚开始的代码,将二维数组初始化为0,接下来按行随机和列随机的方式随机布下指定数目的雷的数量,这样,我们 开始的而且也是扫雷中 实际的“雷区”(map)也就生成了。
但 终要实现用户交互界面,所以需要将我们的map映射到frame上,并能实现点击交互。我们这里的做法是,使用按钮,觉得使用按钮可以不必再去鼠标点击的区域的具体坐标,直接使用按钮监听器来实现对点击位置的具体把控。
所以我们在交互界面,frame上,我们初始化了JButton数组,然后使用了gridlayout的网格布局将按钮布置上去,刚开始的按钮的外观惨不忍睹,后来才采用了图片的形式来替代默认的按钮外观。这些是外在,内在我们还有很多需要考虑的地方。先记住我们的初始化雷区map是0到9的二维数组。
比如,我们刚开始需要为用户提供一个初始界面,就是都没有被动过的按钮,当用户点击的时候,需要展开区域,还是显示数字,还是显示雷,还是标记此位置为雷,每一个按钮的“状态”即外观都是不一样的,而这些“状态”的改变与我们的map上面的数字不能直接形成一一映射的关系,也就是说,我们状态需要改变,但是我们的map是我们基本的雷区,是不能被改变的,而且0到9的范围有10种状态选择,但我们的交互界面需要的状态变化有13种,所以为了解决这种问题,我们引入了一层隐藏层,来表示我们的frame上面的每一块方块的状态变化,我们为隐藏层命名为hiddenmap,同样为我们的int型以便于储存多“状态”。
部分代码:
public void findZero(int i, int j) { if (hiddenmap[i][j] != 0) { if (map[i][j] == 0) { hiddenmap[i][j] = 0; if (i == 0) { if (j == 0) { if (map[i][j + 1] != 0 && map[i][j + 1] != 9) hiddenmap[i][j + 1] = map[i][j + 1]; if (map[i + 1][j] != 0 && map[i + 1][j] != 9) hiddenmap[i + 1][j] = map[i + 1][j]; } else if (j == length - 1) { if (map[i][j - 1] != 0 && map[i][j - 1] != 9) hiddenmap[i][j - 1] = map[i][j - 1]; if (map[i + 1][j] != 0 && map[i + 1][j] != 9) hiddenmap[i + 1][j] = map[i + 1][j]; } else { if (map[i][j - 1] != 0 && map[i][j - 1] != 9) hiddenmap[i][j - 1] = map[i][j - 1]; if (map[i + 1][j] != 0 && map[i + 1][j] != 9) hiddenmap[i + 1][j] = map[i + 1][j]; if (map[i][j + 1] != 0 && map[i][j + 1] != 9) hiddenmap[i][j + 1] = map[i][j + 1]; } } if (i == width - 1) { if (j == 0) { if (map[i][j + 1] != 0 && map[i][j + 1] != 9) hiddenmap[i][j + 1] = map[i][j + 1]; if (map[i - 1][j] != 0 && map[i - 1][j] != 9) hiddenmap[i - 1][j] = map[i - 1][j]; } else if (j == length - 1) { if (map[i - 1][j] != 0 && map[i - 1][j] != 9) hiddenmap[i - 1][j] = map[i - 1][j]; if (map[i][j - 1] != 0 && map[i][j - 1] != 9) hiddenmap[i][j - 1] = map[i][j - 1]; } else { if (map[i][j + 1] != 0 && map[i][j + 1] != 9) hiddenmap[i][j + 1] = map[i][j + 1]; if (map[i - 1][j] != 0 && map[i - 1][j] != 9) hiddenmap[i - 1][j] = map[i - 1][j]; if (map[i][j - 1] != 0 && map[i][j - 1] != 9) hiddenmap[i][j - 1] = map[i][j - 1]; } } if (j == 0) { if (i != 0 && i != width - 1) { if (map[i - 1][j] != 0 && map[i - 1][j] != 9) hiddenmap[i - 1][j] = map[i - 1][j]; if (map[i + 1][j] != 0 && map[i + 1][j] != 9) hiddenmap[i + 1][j] = map[i + 1][j]; if (map[i][j + 1] != 0 && map[i][j + 1] != 9) hiddenmap[i][j + 1] = map[i][j + 1]; } } if (j == length - 1) { if (i != 0 && i != width - 1) { if (map[i - 1][j] != 0 && map[i - 1][j] != 9) hiddenmap[i - 1][j] = map[i - 1][j]; if (map[i + 1][j] != 0 && map[i + 1][j] != 9) hiddenmap[i + 1][j] = map[i + 1][j]; if (map[i][j - 1] != 0 && map[i][j - 1] != 9) hiddenmap[i][j - 1] = map[i][j - 1]; } } if (i != 0 && i != width - 1 && j != 0 && j != length - 1) { if (map[i][j + 1] != 0 && map[i][j + 1] != 9) hiddenmap[i][j + 1] = map[i][j + 1]; if (map[i + 1][j] != 0 && map[i + 1][j] != 9) hiddenmap[i + 1][j] = map[i + 1][j]; if (map[i][j - 1] != 0 && map[i][j - 1] != 9) hiddenmap[i][j - 1] = map[i][j - 1]; if (map[i - 1][j] != 0 && map[i - 1][j] != 9) hiddenmap[i - 1][j] = map[i - 1][j]; } if (j >= 1) findZero(i, j - 1); if (i >= 1) findZero(i - 1, j); if (j <= getLength() - 2) findZero(i, j + 1); if (i <= getWidth() - 2) findZero(i + 1, j); } } }
ObjectInputStream readFile; if (file.isNewOne() == false) { try { readFile = new ObjectInputStream(new FileInputStream(file.getFileName())); this.minefield = (Minefield) readFile.readObject(); readFile.close(); if (minefield.isBoom() == true) { boom.play(); upset.play(); } else { playGame.play(); } } catch (FileNotFoundException e) { //不存在上局时,自动生成一局 this.minefield = new Minefield(file.getWidth(), file.getLength(), file.getLambnumber()); playGame.play(); // JOptionPane.showMessageDialog(null, "您还未开始游戏,不存在上局哦!"); // e.printStackTrace(); } catch (IOException e) { // e.printStackTrace(); } }
四:代码扫描结果及改正
改正步骤
第一种错误:缺少覆盖标志@Override
第二种:缺少大括号
待改进
不足:没有设置计时器和剩余雷的个数。按钮无法改变大小,所以限制了游戏界面无法改变大小。
改良:设置一下计时器和剩余雷的个数,然后将按钮设置成能随着框的改变个改变大小,这就比较可以了。