一步一步写自表达代码——消小球(4)
下一步,我们来实现消除。
消除分两步,第一步消除选中的小球,第二步,降落。
先说消除,
在BallActionListener中进行分支处理即可。
1 public void actionPerformed(ActionEvent e) { 2 Game game = Game.getInstance(); 3 if (game.isSelectedBall(x, y)) { 4 game.destroySelectedBalls(); 5 } else { 6 game.startSelect(x, y); 7 } 8 EventDispatcher.send(Event.UPDATE_BALLS); 9 }
然后实现上述两个新增的方法:
isSelectedBall(int, int)和destroySelectedBalls()
1 public boolean isSelectedBall(int x, int y) { 2 return grid.balls[y][x].selected && marked.size() > 1; 3 } 4 5 public void destroySelectedBalls() { 6 Set<Integer> set = marked.keySet(); 7 Iterator<Integer> iterator = set.iterator(); 8 while (iterator.hasNext()) { 9 Integer key = iterator.next(); 10 grid.balls[key / 12][key % 12].destroyed = true; 11 } 12 marked.clear(); 13 }
然后更新显示部分,对于destroyed的小球做特殊处理。
对MainFrame.render()进行修改:
1 public void render() { 2 Ball[][] balls = Game.getInstance().grid.balls; 3 for (int y = 0; y < 12; y++) { 4 for (int x = 0; x < 12; x++) { 5 if (balls[y][x].destroyed) { 6 this.balls[y][x].setBorder(null); 7 this.balls[y][x].setIcon(null); 8 continue; 9 } 10 if (balls[y][x].selected) { 11 this.balls[y][x].setBorder(BorderFactory 12 .createLineBorder(Color.CYAN)); 13 } else { 14 this.balls[y][x].setBorder(null); 15 } 16 this.balls[y][x].setIcon(balls[y][x].color.getImageIcon()); 17 this.balls[y][x].invalidate(); 18 } 19 } 20 }
至此,消除动作已经完成。
然后,开始进行降落处理。
降落的时候要将所有消除掉的空白格子向上浮起,直到所有的有颜色的都降落下来为止。
那么算法就应该是,按照先列后行的顺序,从右下向左上遍历,遇到空白单元格就向上浮起,直到当前单元格不再是空白单元格为止。另外要防止无限循环。相应代码如下:
1 public void fallDown() { 2 for (int x = 11; x >= 0; x--) { 3 for (int y = 11; y >= 0; y--) { 4 while (swapLine(x, y)) 5 ; 6 } 7 } 8 } 9 10 private boolean swapLine(int x, int y) { 11 boolean frozen = false; 12 if (grid.balls[y][x].destroyed) { 13 for (int i = y; i > 0; i--) { 14 frozen |= bubbleUp(x, i); 15 } 16 } 17 return frozen; 18 } 19 20 private boolean bubbleUp(int x, int y) { 21 Ball temp = grid.balls[y][x]; 22 grid.balls[y][x] = grid.balls[y - 1][x]; 23 grid.balls[y - 1][x] = temp; 24 return !grid.balls[y][x].destroyed; 25 }
然后就是垂直某列如果为空白的时候应该向右并拢,同样,也要注意防止死循环。
1 public void alignRight() { 2 int emptyColumnIndex = -1; 3 while (true) { 4 emptyColumnIndex = getEmptyColumnIndex(); 5 if (emptyColumnIndex == -1) { 6 break; 7 } 8 moveColumnRight(emptyColumnIndex); 9 } 10 } 11 12 private int getEmptyColumnIndex() { 13 for (int x = 1; x < 11; x++) { 14 if (isEmptyColumn(x) && !isEmptyColumn(x - 1)) { 15 System.out.println("empty@" + x); 16 return x; 17 } 18 } 19 return -1; 20 } 21 22 private void moveColumnRight(int x) { 23 for (int i = x; i > 0; i--) { 24 for (int y = 0; y < 12; y++) { 25 grid.balls[y][i] = grid.balls[y][i - 1]; 26 } 27 } 28 for (int y = 0; y < 12; y++) { 29 grid.balls[y][0].destroyed = true; 30 } 31 } 32 33 private boolean isEmptyColumn(int x) { 34 for (int y = 0; y < 12; y++) { 35 if (!grid.balls[y][x].destroyed) { 36 return false; 37 } 38 } 39 return true; 40 }
至此,消除同色小球的处理已经完成。
但是,我们发现,还有如下问题:
1. GridBagLayout导致了小球消除以后分散显示。
2. 没有后悔功能。
3. 有些算法的确效率很低。
4. 没有用到control下面的类。
我们将在以后的章节中逐步阐述。
-----------------------------------------------------------------
现为独立咨询师。为软件企业或者其他企业的软件采购部门提供咨询,帮助改进软件开发流程,员工技术能力提升,以帮助企业在质量成本交货期三方面得到改善。