基于ARM9开发板的扫雷游戏设计 view->setScene(scene);
ui->setupUi(this)中this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。也就是说,即使你没有写上this指针,编译器在编译的时候也是加上this的,它作为非静态成员函数的隐含形参,对各成员的访问均通过this进行。这句程序的意思为设置本ui界面作为ui界面。
view = new QGraphicsView,scene = new SweepMinesScene为在内存中开辟一段空间来存放视图和场景。
setCentralWidget(view),view->setScene(scene)是设置好游戏的视图和场景的意思。
3.1.3 视图中控件和动作的设计
在游戏中我们能看到菜单栏有一些“菜单”、“帮助”、“退出”等控件,点击控件就可以退出,重新开始游戏,查看版本信息等功能。
打开ui界面,在界面中的“在这里输入”中输入你想要设计的控件,本设计选择“菜单”,“帮助”两个控件,同时设计“选项”,“退出”,“开局”,“关于”等几个动作,“菜单”和“帮助”为主控件,“开局”,“选项”,“退出”为“菜单”主控件中的动作,点击“菜单”我们就能看到下面三个动作,这三个动作为功能动作,“开局”动作为开始游戏的意思,“选项”动作为我们设置游戏的行和列,“退出”动作为退出游戏。“关于”为“帮助”下的动作,打开“关于”可以看到此游戏的版本号和信息。
在ui界面的下方我们可以看到控件和动作的信息,我们应该把动作与游戏结合起来,在ui界面的下方列出了动作,双击动作,会看到如图3-3所示的界面。
3-3 控件设计的界面
14
基于ANM9开发板的扫雷游戏设计 图中文本为动作的名称。对象名称为程序中的名称,因为QT的程序对中文的支持低,同时不支持中文路径,所以得用英文名称。工具提示为我们在游戏时看到的名字。图标可以为每个动作选择一张图片作为图标,同时我们可以拖动图标放到菜单栏下面的快捷键栏,因为“开局”这动作在游戏中用得比较频繁,所以我们为开局设计一个快捷图标。下面快捷键可以为每个动作设计一个快捷键。如图3-4所示。
图3-4 控件的展示
菜单栏下面为快捷键栏,我们把开局的快捷图标拖到快捷键栏,点击图标就可以开局。
控件设计好之后接下来就是怎么把控件和程序结合起来。如图3-5所示,
3-5 ui界面中的控件信息
图3-5为ui界面下面的动作信息,我们右键点击动作然后选择“转到槽”,就会弹出一个如图3-6所示的对话框。
图3-6 选择信号的对话框
15
基于ARM9开发板的扫雷游戏设计 我们选择triggered信号。
这样就为此动作自动设置了信号槽。在程序的头文件的类中会自动生成private slots的成员,程序如下所示:
private slots:
void on_actionOption_triggered(); void on_actionExit_triggered(); void on_actionAbout_triggered(); void on_actionStart_triggered();
同时会在源文件中会自动生成此四个函数。我们用程序编写函数的功能就可以把动作的功能实现。我们将在3.4程序功能块中再介绍。
3.2 STL数据的设计
3.2.1 STL的设计
STL数据是扫雷游戏最底层的数据,是控制扫雷功能的核心数据。用数字-1代表雷,数字0代表空格,周围没有雷的意思,数字1-8为代表周围有1-8个雷的意思。再把底层数据和上层的图片结合起来就形成了扫雷的基本机构。当我们点击一张图片是,如果图片下面的是数字0,表示你点中了雷,然后雷全部爆炸,如果下面是数字0,则用函数递归自动把周围的图片翻开,知道翻到有数字为止。如果下面是数字1-8,则只翻开此图片,代表周围1-8个雷。
我们用C++中的容器来实现底层的数据结构,C++中的容器类包括“顺序存储结构”和“关联存储结构”,前者包括vector,list,deque等;后者包括set,map,multiset,multimap等。若需要存储的元素数在编译器间就可以确定,可以使用数组来存储,否则,就需要用到容器类了。C++中容器类有很多个,我们用vector容器,vector容器是连续存储结构,每个元素在内存上是连续的,支持高效的随机访问和在尾端插入/删除操作,但其他位置的插入/删除操作效率低下;
先建立一个类field,会自动生成field.cpp的源文件和field.h的头文件,然后在field类中建立一个名叫cells的vector类STL容器,程序如下:
typedef vector
CellMatrix cells;
16
基于ANM9开发板的扫雷游戏设计
int width; int height; int mines;
typedef声明,简称typedef,为现有类型创建一个新的名字,或称为类型别名,在结构体定义,还有一些数组等地方都大量的用到。我们先声明一个int型的vector单列的容器,取类型别名为CellColumn,类似于数组中的一维数组。因为扫雷游戏是一个二维的图形界面,所以我们得创建一个二维的数据。接下来声明一个多列的容器,即容器中包含容器,声明一个CellColumn型的容器,取别名为CellMatrix,类似于二维数组。
vector
width为扫雷游戏界面的宽,height为扫雷游戏界面的高,mines为扫雷游戏中雷的个数。如图3-7所示:
3-7 底层数据的排版
height cells[0][0] cells[0][1] cells[0][2] ...... cells[0][n] cells[1][0] cells[1][1] cells[1][2] ...... cells[1][n] cells[n][0] cells[n][1] cells[n][2] ...... cells[n][n] width 图中每一列为一个单列的容器,把几个单列的容器组合起来就组成多列的容器。
void initCells()函数为容器扩容,即我们需要一个多大的容器来存放数据,函数程序如下:
for(int x=0; x cells.push_back(CellColumn(height)); } 我们用扫雷界面的高来作为单列的容器中的数值,再用一个for循环来把单列的容器堆入多列的容器中,用扫雷界面的宽作为for循环的循环次数。 17 基于ARM9开发板的扫雷游戏设计 3.2.2 数据的设计 容器建立好了,但是扫雷游戏的底层数据怎么建立呢?我们利用void deployMines()函数来建立底层数据。我们首先把STL容器中的数值都清零,防止数据异常。如以下程序所示: for(int x=0; x for(int y=0; y cells[x][y] = 0; 当你翻开一张数值型图片时,显示的是此数值周围的雷数,所以当我们每布下一个雷就要把周围的数据加1,除了有雷的部分。周围数值加1的程序如下: void Field::updateSurrounding(int x, int y) { if(x>=0&&x if(cells[x][y] != -1) cells[x][y]++; } } 编写一个void updateSurrounding(int x, int y)函数来把周围的数值加1,只要此数值不是雷,即不等于-1,就把此数据加1,。 我们利用rand函数来产生随机数,我们把雷随机的布在我们扫雷界面的区域。 在布雷的时候每布一个雷就要自减1,当数值为0时布雷完成,但是雷的数量是一个不变的数据,我们必须保护mines这个变量。所以先把雷的数值赋给一个变量cpmines,我们来操作那个变量这样就不会改变雷的数目。同时用while循环来控制布雷的个数。 用两个rand()函数来产生两个随机数,一个用来表示雷的位置的横坐标,一个用来表示雷的位置的纵坐标。产生随机数后为了不让数值大于雷区域的宽和高,我们把产生的随机数来分别对雷区域的宽和高取余,这样就可以保证产生的数值不会大于雷区域的宽和高。 当产生完随机数之后我们就得到了两个坐标,把该坐标的数值置为-1,代表此位置布下了一个雷。 然后再调用void updateSurrounding(int x, int y)函数把雷周围八个 18