一步一步写自表达代码——消小球(1)

在写了这么多理论之后,读者都对实践比较感兴趣。那么如何写自表达代码呢?

我们跳开理论部分(以后我会补充理论部分),直接进入实践部分,从本章开始,将以消小球这个游戏为例,以Java代码为样本,引导大家一步一步书写自表达式代码。

消小球是一款见于Windows Mobile上的游戏,在12x12的方格子里随即放置各种颜色的小球。选中至少两个相邻的同颜色小球,再次点击,就消除选中的小球,消除的小球的个数累加作为得到的分数,消掉小球以后上面的小球会降落,某列被消除以后,会将其左边的列拉过来,直到无法继续消除位置。

 

好了,游戏规则介绍完毕。那么我们开始在Eclipse里建立工程。Java工程,名称BubbleBreaker。

然后根据MVC模式建立包结构。

Model

     在这个游戏里,需要方格(Grid),小球(Ball),还有分数(Score),另外需要一个整体的游戏对象(Game),用来存放这些数据。

Control

     在这个游戏里,需要选择(Select),消除(Clear),降落(FallDown),判定游戏是否结束(GameOver)等行为。

View

     在这个游戏里,需要显示游戏主要区域(MainFrame)用来显示小球部分,以及辅助区域(HelperFrame)用来显示分数,选中个数等。以及一个最外边的大容器(GamePad)。用来组装所有的内容。

下面我们为Model建立各种属性。在建立各种属性的时候,我们需要开启Eclipse的保存动作 - 自动格式化以及自动引入Imports的功能,
具体方法是,选中该工程,右键选中属性,打开下面的对话框,按照下图勾选选项,并保存。

小球(Ball)

     颜色 - 我们采用Java提供的颜色Color作为其颜色属性类型。Color color;

     位置 - 整数。 int x, int y;

     状态 - State - 一个枚举类型,应该至少包括NORMAL,SELECTED, DESTROYED几种状态,也许还会增加。注意,由于该State是属于小球的,所以它是一个内部枚举。因为在外部引用时可以看到Ball.State.SELECTED样式的调用。这是自表达代码的一个重要原则——找到合适的归宿。

     那么Ball类的代码如下:

 1 package org.stephen.bubblebreaker.model;
 2 
 3 import java.awt.Color;
 4 
 5 public class Ball {
 6 
 7     Color color;
 8 
 9     int x;
10 
11     int y;
12 
13     State state;
14 
15     enum State {
16         NORMAL, SELECTED, DESTROYED;
17     }
18 }

好的,这里我们没有对public,protected,private进行定义,暂时保留这些内容,留待以后决定。

方格(Grid)

方格中包含的就是12x12个小球。那么,如何存放呢?

我们可以采用List,也可以采用数组[],可以采用1维的,也可以是2维的。

目前我们不知道哪个更合理,先选用数组吧,比较直观。这是自表达代码的另一个原则——先选取简单的模型,以后可以变更。

Grid类的代码如下:

1 package org.stephen.bubblebreaker.model;
2 
3 public class Grid {
4 
5     Ball[][] balls;
6 }

分数(Score)

应该包含:当前选中小球的总分数(selected),已经获得的分数(current),最高分数(highest),平均分数(average),已玩局数(playCount)。

 1 package org.stephen.bubblebreaker.model;
 2 
 3 public class Score {
 4 
 5     int selected;
 6 
 7     int current;
 8 
 9     int highest;
10 
11     int average;
12 
13     int playCount;
14 }

最后是游戏(Game)。

这个对象是全局唯一的,用来存放其他数据,所以,它应该是单例模式,从外部可以随时通过getInstance获得数据对象。另外,它应该包含其他数据,就目前模型来看,它应该包含:

Grid和Score两个对象。注意:Ball是包含在Grid中的。

好了,到此,Model的属性已经初步定义完成了。

然后,来看View。我们用Swing来实现显示。

GamePad是主入口,所以,它应该包含main方法。其方法的内容就是建立GamePad对象,并显示。

由此,GamePad应该是JFrame的一个子类。而它包含了由MainFrame和HelperFrame两个对象构成的现实和区域,这两个类应该是JPanel的子类。

我们采用40x40的单元格代表一个小球,那么整个Grid的尺寸是480x480,Helper放在上部,那么整个GamePad的尺寸是

480x600。然后,我们逐个加入控件。

最后,我们在关闭工作中,加入exit代码,确保点击右上角红叉关闭的时候能够正确的退出应用程序。

相应代码如下:

GamePad:

 1 package org.stephen.bubblebreaker.view;
 2 
 3 import javax.swing.JFrame;
 4 
 5 public class GamePad extends JFrame {
 6 
 7     MainFrame main = new MainFrame();
 8     HelperFrame helper = new HelperFrame();
 9 
10     public GamePad() {
11 
12     }
13 
14     public void build() {
15         this.setTitle("Bubble Breaker");
16         this.setResizable(false);
17         this.setLayout(null);
18         this.setSize(480, 620);
19         main.build();
20         helper.build();
21         this.add(helper);
22         this.add(main);
23         this.setDefaultCloseOperation(EXIT_ON_CLOSE);
24     }
25 
26     public static void main(String[] args) {
27         GamePad pad = new GamePad();
28         pad.build();
29         pad.setVisible(true);
30     }
31 
32 }

MainFrame

 1 package org.stephen.bubblebreaker.view;
 2 
 3 import java.awt.Color;
 4 import java.awt.GridBagConstraints;
 5 import java.awt.GridBagLayout;
 6 
 7 import javax.swing.BorderFactory;
 8 import javax.swing.JLabel;
 9 import javax.swing.JPanel;
10 
11 public class MainFrame extends JPanel {
12 
13     JLabel[][] balls;
14 
15     public MainFrame() {
16 
17     }
18 
19     public void build() {
20         GridBagLayout grid = new GridBagLayout();
21         this.setLayout(grid);
22         this.setSize(480, 480);
23         balls = new JLabel[12][12];
24 
25         GridBagConstraints constraints = new GridBagConstraints();
26         constraints.weightx = 100;
27         constraints.weighty = 100;
28         for (int y = 0; y < 12; y++) {
29             for (int x = 0; x < 12; x++) {
30                 JLabel ball = new JLabel("AD");
31                 ball.setBorder(BorderFactory.createLineBorder(Color.blue));
32                 ball.setSize(40, 40);
33                 constraints.gridwidth = 40;
34                 constraints.gridheight = 40;
35                 constraints.gridx = x * 40;
36                 constraints.gridy = y * 40;
37                 this.add(ball, constraints);
38             }
39         }
40     }
41 }

HelperFrame

 1 package org.stephen.bubblebreaker.view;
 2 
 3 import javax.swing.JLabel;
 4 import javax.swing.JPanel;
 5 
 6 public class HelperFrame extends JPanel {
 7 
 8     JLabel hintCurrent;
 9     JLabel current;
10 
11     JLabel hintSelected;
12     JLabel selected;
13 
14     JLabel hintHighest;
15     JLabel highest;
16 
17     JLabel hintAverage;
18     JLabel average;
19 
20     JLabel hintPlayCount;
21     JLabel playCount;
22 
23     public HelperFrame() {
24 
25     }
26 
27     public void build() {
28         this.setSize(480, 120);
29         hintCurrent = new JLabel("Current");
30         this.add(hintCurrent);
31 
32         current = new JLabel("0");
33         this.add(current);
34 
35         hintSelected = new JLabel("Selected");
36         this.add(hintSelected);
37 
38         hintSelected = new JLabel("0");
39         this.add(hintSelected);
40 
41         hintHighest = new JLabel("Highest");
42         this.add(hintHighest);
43 
44         highest = new JLabel("0");
45         this.add(highest);
46 
47         hintAverage = new JLabel("Average");
48         this.add(hintAverage);
49 
50         average = new JLabel("0");
51         this.add(average);
52 
53         hintPlayCount = new JLabel("PlayCount");
54         this.add(hintPlayCount);
55 
56         playCount = new JLabel("0");
57         this.add(playCount);
58     }
59 }

到此,我们运行一下,可以看到下面的界面。

可以看到,计算的数值和显示之间是有一定误差的。并且,

底部留下了一定的空白面积——我们还忘记了控制面板:开始、重新开始、清空、退出等按钮。正好在底下加上。

顺便让屏幕居中,并且,修正了一个布局上的错误——Grid也是从0,0开始的。

重新调整完的代码如下:

GamePad.java

 1 package org.stephen.bubblebreaker.view;
 2 
 3 import java.awt.GraphicsEnvironment;
 4 import java.awt.Point;
 5 
 6 import javax.swing.JFrame;
 7 
 8 public class GamePad extends JFrame {
 9 
10     MainFrame main = new MainFrame();
11     HelperFrame helper = new HelperFrame();
12     ControlFrame control = new ControlFrame();
13 
14     public GamePad() {
15 
16     }
17 
18     public void build() {
19         this.setTitle("Bubble Breaker");
20         int width = 480;
21         int height = 600;
22         Point p = GraphicsEnvironment.getLocalGraphicsEnvironment()
23                 .getCenterPoint();
24         this.setLocation(p.x - width / 2, p.y - height / 2);
25         this.setResizable(false);
26         this.setLayout(null);
27         this.setSize(width, height);
28 
29         main.build();
30         helper.build();
31         control.build();
32 
33         this.add(helper);
34         helper.setLocation(0, 0);
35         this.add(main);
36         main.setLocation(0, 40);
37         this.add(control);
38         control.setLocation(0, 520);
39 
40         this.setDefaultCloseOperation(EXIT_ON_CLOSE);
41     }
42 
43     public static void main(String[] args) {
44         GamePad pad = new GamePad();
45         pad.build();
46         pad.setVisible(true);
47     }
48 
49 }

MainFrame.java

 1 package org.stephen.bubblebreaker.view;
 2 
 3 import java.awt.Color;
 4 import java.awt.GridBagConstraints;
 5 import java.awt.GridBagLayout;
 6 
 7 import javax.swing.BorderFactory;
 8 import javax.swing.JLabel;
 9 import javax.swing.JPanel;
10 
11 public class MainFrame extends JPanel {
12 
13     JLabel[][] balls;
14 
15     public MainFrame() {
16 
17     }
18 
19     public void build() {
20         GridBagLayout grid = new GridBagLayout();
21         this.setLayout(grid);
22         this.setSize(480, 480);
23         balls = new JLabel[12][12];
24 
25         GridBagConstraints constraints = new GridBagConstraints();
26         constraints.weightx = 100;
27         constraints.weighty = 100;
28         for (int y = 0; y < 12; y++) {
29             for (int x = 0; x < 12; x++) {
30                 JLabel ball = new JLabel("AD");
31                 ball.setBorder(BorderFactory.createLineBorder(Color.blue));
32                 ball.setSize(40, 40);
33                 constraints.gridwidth = 40;
34                 constraints.gridheight = 40;
35                 constraints.gridx = x * 40;
36                 constraints.gridy = y * 40;
37                 this.add(ball, constraints);
38             }
39         }
40     }
41 }

 

HelperFrame.java

 1 package org.stephen.bubblebreaker.view;
 2 
 3 import javax.swing.JLabel;
 4 import javax.swing.JPanel;
 5 
 6 public class HelperFrame extends JPanel {
 7 
 8     JLabel hintCurrent;
 9     JLabel current;
10 
11     JLabel hintSelected;
12     JLabel selected;
13 
14     JLabel hintHighest;
15     JLabel highest;
16 
17     JLabel hintAverage;
18     JLabel average;
19 
20     JLabel hintPlayCount;
21     JLabel playCount;
22 
23     public HelperFrame() {
24 
25     }
26 
27     public void build() {
28         this.setSize(480, 40);
29         hintCurrent = new JLabel("Current");
30         this.add(hintCurrent);
31 
32         current = new JLabel("0");
33         this.add(current);
34 
35         hintSelected = new JLabel("Selected");
36         this.add(hintSelected);
37 
38         hintSelected = new JLabel("0");
39         this.add(hintSelected);
40 
41         hintHighest = new JLabel("Highest");
42         this.add(hintHighest);
43 
44         highest = new JLabel("0");
45         this.add(highest);
46 
47         hintAverage = new JLabel("Average");
48         this.add(hintAverage);
49 
50         average = new JLabel("0");
51         this.add(average);
52 
53         hintPlayCount = new JLabel("PlayCount");
54         this.add(hintPlayCount);
55 
56         playCount = new JLabel("0");
57         this.add(playCount);
58     }
59 }

ControlFrame.java

 1 package org.stephen.bubblebreaker.view;
 2 
 3 import javax.swing.JButton;
 4 import javax.swing.JPanel;
 5 
 6 public class ControlFrame extends JPanel {
 7 
 8     JButton start = new JButton("Start");
 9     JButton clear = new JButton("Clear");
10     JButton exit = new JButton("Exit");
11 
12     public ControlFrame() {
13 
14     }
15 
16     public void build() {
17         this.setSize(480, 120);
18         this.add(start);
19         this.add(clear);
20         this.add(exit);
21     }
22 }

显示界面:

好的,显示就讲到这里,下一讲,我们讲述如何进行操控。

 

 

 

 

posted @ 2012-11-27 13:24  史蒂芬.王  阅读(470)  评论(0编辑  收藏  举报