推荐QT的俄罗斯方块设计 精品.docx
《推荐QT的俄罗斯方块设计 精品.docx》由会员分享,可在线阅读,更多相关《推荐QT的俄罗斯方块设计 精品.docx(34页珍藏版)》请在冰点文库上搜索。
推荐QT的俄罗斯方块设计精品
摘要
Qt是一个跨平台的C++图形用户界面应用程序框架。
本程序利用Qt提供的相关类,实现了俄罗斯方块的基本功能。
关键字
QT、嵌入式、软件开发
1功能说明
Ø支持俄罗斯方块游戏的基本功能
Ø支持虚拟按键
2开发环境
操作系统:
ubuntu10.04LTS
开发工具:
gnu编译工具链(gcc等)、QtCreator、Qt4.6.2
2.1Qt简介
Qt是跨平台的应用程序和UI框架。
它包括跨平台类库、集成开发工具和跨平台IDE。
使用Qt,只需一次性开发应用程序,无须重新编写源代码,便可跨不同桌面和嵌入式操作系统部署这些应用程序。
2.2Qt安装
1.Qt官网()上有完整的SDK下载,下载开发平台的SDK,下载完成后点击即可安装
2.如果是使用Linux系统,也可能通过命令行进行安装,以ubuntu10.04LTS为例:
sudoapt-getinstallqt4-dev-toolsqtcreatorqt4-docqt4-qtconfigqt-demos
3.如果希望构建嵌入式Qt开发平台,则需要参考相关开发板的说明,一般是先制作交叉编译工具链、再交叉编译一个用于目标板的Qt库,这里不再详述
2.3Qt开发基础
2.3.1Qt对象与对象树
QObject是所有Qt类的基类。
QObject组织成为对象树。
当你创建QObject时,将另外的对象作为其父对象,这个对象就被加入其父对象的children()列表,并且当父对象销毁时,这个对象也能够被销毁。
事实证明,这种实现方法非常适合GUI对象。
例如,一个QShortcut(键盘快捷键)对象是相关窗口的子对象,所以当用户关闭窗口时,这个对象也能够被删除。
QWidget作为所有能够显示在屏幕上的组件的父类,扩展了这种父子关系。
一个子对象通常也成为一个子组件,就是说,它被显示在父组件的坐标系统中,受到父组件的边界影响可能会有剪切等等。
例如,当应用程序销毁掉已关闭的消息对话框时,对话框上面的按钮和标签一起被销毁,就像我们希望的那样,因为这些按钮和标签都是对话框的子对象。
2.3.2信号与槽
在GUI编程中,当我们改变了一个组件,我们经常需要通知另外的一个组件。
更一般地,我们希望任何类型的对象都能够与另外的对象通讯。
例如,如果用户点击了关闭按钮,我们希望窗口的close()函数被调用。
早期工具库对这种通讯使用回调实现。
回调是一个指向一个函数的指针,所以如果你希望某种事件发生的时候,处理函数获得通知,你就需要将指向另外函数的指针(也就是这个回调)传递给处理函数。
这样,处理函数就会在合适的时候调用回调函数。
回调有两个明显的缺点:
第一,它们不是类型安全的。
我们不能保证处理函数传递给回调函数的参数都是正确的。
第二,回调函数和处理函数紧密地耦合在一起,因为处理函数必须知道哪一个函数被回调。
在Qt中,我们有回调技术之外的选择:
信号槽。
当特定事件发出时,一个信号会被发出。
Qt组件有很多预定义的信号,同时,我们也可以通过继承这些组件,添加自定义的信号。
槽则能够响应特定信号的函数。
Qt组件有很多预定义的槽,但是更常见的是,通过继承组件添加你自己的槽,以便你能够按照自己的方式处理信号。
信号槽机制是类型安全的:
信号的签名必须同接受该信号的槽的签名一致(实际上,槽的参数个数可以比信号少,因为槽能够忽略信号定义的多出来的参数)。
既然签名都是兼容的,那么编译器就可以帮助我们找出不匹配的地方。
信号和槽是松耦合的:
发出信号的类不知道也不关心哪些槽连接到它的信号。
Qt的信号槽机制保证了,如果你把一个信号同一个槽连接,那么在正确的时间,槽能够接收到信号的参数并且被调用。
信号和槽都可以有任意类型的任意个数的参数。
它们全部都是类型安全的。
所有继承自QObject或者它的一个子类(例如QWidget)都可以包含信号槽。
信号在对象改变其状态,并且这个状态可能有别的对象关心时被发出。
这就是这个对象为和别的对象交互所做的所有工作。
它并不知道也不关心有没有别的对象正在接收它发出的信号。
这是真正的信息封装,保证了这个对象能够成为一个组件。
槽能够被用于接收信号,也能够像普通函数一样使用。
正如一个对象并不知道究竟有没有别的对象正在接收它的信号一样,一个槽也不知道有没有信号与它相连。
这保证了使用Qt可以创建真正相互独立的组件。
你可以将任意多个信号连接到同一个槽上,也可能将一个信号连接任意多个槽。
同时,也能够直接将一个信号与另一个信号相连(这会使第一个信号发出时,马上发出第二个信号)。
总之,信号槽建立起一种非常强大的组件编程机制。
2.3.3事件
在Qt中,事件是作为对象处理的,所有事件对象继承自抽象类QEvent。
此类用来表示程序内部发生或者来自于外部但应用程序应该知道的动作。
事件能够能过被QObject的子类接受或者处理,但是通常用在与组件有关的应用中。
本文档主要阐述了在一个典型应用中的事件接收与处理。
当一个事件产生时,Qt通过实例化一个QEvent的合适的子类来表示它,然后通过调用event()函数发送给QObject的实例(或者它的子类)。
event()函数本身并不会处理事件,根据事件类型,它将调用相应的事件处理函数,并且返回事件被接受还是被忽略。
一些事件,比如QMouseEvent和QKeyEvent,来自窗口系统;有的,比如QTimerEvent,来自于其他事件源;另外一些则来自应用程序本身。
通常事件的处理需要调用一个虚函数。
比如,QPaintEvent事件的处理需要调用QWidget:
:
paintEvent()函数。
这个虚函数负责做出适当的响应,通常是用来重绘组件。
如果你在自己的函数中并不打算实现所有的处理,你可以调用基类的实现。
3系统设计
3.1需求分析
v可随机生成7种基本方块单元
v不同的方块单元具备不同的颜色
v基本方块单元在移动时支持两种操作:
旋转、移动
v具备计分及升级系统
v支持虚拟按键
3.2框架设计
3.2.1俄罗斯方块基本规则
一个用于摆放小型正方形的平面虚拟场地,其标准大小:
行宽为10,列高为20,以每个小正方形为单位
一组由4个小型正方形组成的规则图形,英文称为Tetromino,中文通称为方块共有7种,分别以S、Z、L、J、I、O、T这7个字母的形状来命名
随机发生器不断地输出单个方块到场地顶部,以一定的规则进行移动、旋转、下落和摆放,锁定并填充到场地中。
每次摆放如果将场地的一行或多行完全填满,则组成这些行的所有小正方形将被消除,并且以此来换取一定的积分或者其他形式的奖励。
而未被消除的方块会一直累积,并对后来的方块摆放造成各种影响
如果未被消除的方块堆放的高度超过场地所规定的最大高度(并不一定是20或者玩家所能见到的高度),则游戏结束
3.2.2系统模块
如上图所示,系统可由以下几个模块组成:
v虚拟显示屏:
为系统核心模块,负责游戏元素的显示、游戏逻辑的执行、以及游戏状态的维护、接收操作模块的操作信息、为辅助显示模块提供必要的信息
v辅助显示模块:
显示下一个方块单元的类型、当前分数、当前等级
v操作区模块:
为用户提供操作按键
3.3系统实现
系统源文件布局如下:
ØHTetris.pro:
系统工程文件
ØHTetrisWindow.h:
HTetrisWindow类声明头文件
ØHTetrisPiece.h:
HTetrisPiece类声明头文件
ØHTetrisBoard.h:
HTetrisBoard类声明头文件
Øtetris.qrc:
系统资源文件,存放了表示方向的图像数据
ØHTetrisWindow.cpp:
HTetrisWindow类的实现
ØHTetrisPiece.cpp:
HTetrisPiece类的实现
ØHTetrisBoard.cpp:
HTetrisBoard类的实现
Ømain.cpp:
程序入口
main.cpp中初始化一个HTetrisWindow实例,并使其显示。
HTetrisWindow对应程序窗口,它包含一个游戏显示区(HTetrisBoard)、辅助显示区、及一些按键,HTetrisWindow在自身的构造函数中完成对这些界面元素的初始化及布局工作,同时建立起必要的信号-槽连接。
HTetrisPiece类表示基本方块单元,总共有7种,即I、T、J、L、O、Z、S,用HTetrisPieceShape来标识方块类型。
HTetrisPiece提供了设置方块形状、设置旋转、获取方块信息的一些公共成员函数。
HTetrisPiece使用coords[4][2]这个二维数组来存储方块的形状信息,这个数组的每行表示一个点的坐标。
HTetrisBoard是整个程序的核心,相对前两个类,这个类要复杂很多。
它提供了如下几个槽:
start()、pause()、moveRight()、moveLeft()、moveDown()、rotateRight()、rotateLeft()。
提供了scoreChanged与levelChanged两个信号。
paintEvent负责整个HTetrisBoard的重绘。
removeFullLines负责判断是否某行全部为方块,如果是,则把该行消除,同时添加一定分数及经验。
4系统测试
程序的运行界面如上图所示,经测试,该程序具备了俄罗斯方块游戏的基本功能。
5课程设计总结
通过这次嵌入式实验,我对嵌入式技术有了更加深入的了解,在老师悉心帮助下,和其他同学的共同努力下,我们最终圆满地完成了这次课程设计。
但是实验中,也暴露了自己在软件运用方面的不足和缺点,以后在这方面上认真学习和研究,争取在毕业之前能更上一层楼。
6附录
6.1参考资料
[1]
[2]CplusplusGUIProgrammingwithQt42ndEdition
[3]
6.2程序源码
/*
*Filename:
main.cpp
*Author:
HaleChan
*Date:
20XX-11-24
*/
#include
#include"HTetrisWindow.h"
intmain(intargc,char*argv[])
{
QApplicationapp(argc,argv);
HTetrisWindowwindow;
window.show();
qsrand(QTime(0,0,0).secsTo(QTime:
:
currentTime()));
returnapp.exec();
}
/*
*Filename:
HTetrisWindow.h
*Author:
HaleChan
*Date:
20XX-11-24
*/
#ifndefHTetrisWINDOW_H
#defineHTetrisWINDOW_H
#include
classHTetrisBoard;
classQLabel;
classQLCDNumber;
classQPushButton;
classHTetrisWindow:
publicQWidget
{
Q_OBJECT
public:
HTetrisWindow();
private:
QLabel*createLabel(constQString&text);
HTetrisBoard*board;
QLabel*nextPieceLabel;
QLCDNumber*scoreLcd;
QLCDNumber*levelLcd;
QPushButton*leftButton;
QPushButton*rightButton;
QPushButton*upButton;
QPushButton*downButton;
QPushButton*aButton;
QPushButton*bButton;
};
#endif//HTetrisWINDOW_H
/*
*Filename:
HTetrisWindow.cpp
*Author:
HaleChan
*Date:
20XX-11-24
*/
#include"HTetrisWindow.h"
#include"HTetrisBoard.h"
#include
HTetrisWindow:
:
HTetrisWindow()
{
board=newHTetrisBoard;
nextPieceLabel=newQLabel;
nextPieceLabel->setFrameStyle(QFrame:
:
Box|QFrame:
:
Raised);
nextPieceLabel->setAlignment(Qt:
:
AlignCenter);
nextPieceLabel->setBaseSize(60,60);
nextPieceLabel->setMinimumSize(60,60);
nextPieceLabel->setMaximumSize(60,60);
nextPieceLabel->setSizePolicy(QSizePolicy:
:
Fixed,QSizePolicy:
:
Fixed);
board->setNextPieceLabel(nextPieceLabel);
scoreLcd=newQLCDNumber(6);
scoreLcd->setSegmentStyle(QLCDNumber:
:
Filled);
scoreLcd->setFixedWidth(70);
levelLcd=newQLCDNumber
(1);
levelLcd->setSegmentStyle(QLCDNumber:
:
Filled);
levelLcd->setFixedWidth(70);
leftButton=newQPushButton;
leftButton->setAutoRepeat(true);
leftButton->setIcon(QIcon(":
/images/left.png"));
rightButton=newQPushButton;
rightButton->setAutoRepeat(true);
rightButton->setIcon(QIcon(":
/images/right.png"));
upButton=newQPushButton;
upButton->setIcon(QIcon(":
/images/up.png"));
downButton=newQPushButton;
downButton->setAutoRepeat(true);
downButton->setIcon(QIcon(":
/images/down.png"));
aButton=newQPushButton(tr("A"));
aButton->setFixedWidth(50);
bButton=newQPushButton(tr("B"));
bButton->setFixedWidth(50);
connect(leftButton,SIGNAL(clicked()),board,SLOT(moveLeft()));
connect(rightButton,SIGNAL(clicked()),board,SLOT(moveRight()));
connect(upButton,SIGNAL(clicked()),board,SLOT(pause()));
connect(downButton,SIGNAL(clicked()),board,SLOT(moveDown()));
connect(aButton,SIGNAL(clicked()),board,SLOT(rotateLeft()));
connect(bButton,SIGNAL(clicked()),board,SLOT(rotateRight()));
connect(board,SIGNAL(levelChanged(int)),levelLcd,SLOT(display(int)));
connect(board,SIGNAL(scoreChanged(int)),scoreLcd,SLOT(display(int)));
QGridLayout*mainLayout=newQGridLayout;
mainLayout->addWidget(board,0,0,7,3,Qt:
:
AlignCenter);
mainLayout->addWidget(createLabel(tr("Next")),0,3,Qt:
:
AlignCenter);
mainLayout->addWidget(nextPieceLabel,1,3,Qt:
:
AlignCenter);
mainLayout->setRowStretch(2,9);
mainLayout->addWidget(createLabel(tr("Score")),3,3,Qt:
:
AlignCenter);
mainLayout->addWidget(scoreLcd,4,3,Qt:
:
AlignHCenter|Qt:
:
AlignTop);
mainLayout->addWidget(createLabel(tr("Level")),5,3,Qt:
:
AlignCenter);
mainLayout->addWidget(levelLcd,6,3,Qt:
:
AlignHCenter|Qt:
:
AlignTop);
mainLayout->addWidget(upButton,7,1,Qt:
:
AlignBottom);
mainLayout->addWidget(aButton,7,3,Qt:
:
AlignBottom|Qt:
:
AlignHCenter);
mainLayout->addWidget(leftButton,8,0,Qt:
:
AlignTop|Qt:
:
AlignLeft);
mainLayout->addWidget(downButton,8,1,Qt:
:
AlignBottom);
mainLayout->addWidget(rightButton,8,2,Qt:
:
AlignRight|Qt:
:
AlignTop);
mainLayout->addWidget(bButton,8,3,Qt:
:
AlignBottom|Qt:
:
AlignHCenter);
mainLayout->setRowMinimumHeight(7,30);
mainLayout->setRowMinimumHeight(8,50);
this->setLayout(mainLayout);
this->setWindowTitle(tr("Tetris"));
this->setFixedSize(240,400);
}
QLabel*HTetrisWindow:
:
createLabel(constQString&text)
{
QLabel*lbl=newQLabel(text);
lbl->setAlignment(Qt:
:
AlignHCenter|Qt:
:
AlignBottom);
returnlbl;
}
/*
*Filename:
HTetrisPiece.h
*Author:
HaleChan
*Date:
20XX-11-24
*/
#ifndefHTetrisPIECE_H
#defineHTetrisPIECE_H
typedefenum{
HTetrisPieceShapeNone,
HTetrisPieceShapeI,
HTetrisPieceShapeT,
HTetrisPieceShapeJ,
HTetrisPieceShapeL,
HTetrisPieceShapeO,
HTetrisPieceShapeZ,
HTetrisPieceShapeS
}HTetrisPieceShape;
typedefenum{
HTetrisPieceRotateZero,
HTetrisPieceRotate90,
HTetrisPieceRotate180,
HTetrisPieceRotate270
}HTetrisPieceRotate;
classHTetrisPiece
{
public:
HTetrisPiece(){setShape(HTetrisPieceShapeNone);}
voidsetRandomShape();
voidsetShape(HTetrisPieceShapeshape);
HTetrisPieceShapeshape()const{returnpieceShape;}
intx(intindex)const{returncoords[index][0];}
inty(intindex)const{returncoords[index][1];}
intminX()const;
intmaxX()const;
intminY()const;
intmaxY()const;
voidsetRotate(HTetrisPieceRotaterotate);
HTetrisPiecepieceFromRotatedLeft()const;
HTetrisPiecepieceFromRotatedRight()const;
private:
voidsetX(intindex,intx){coords[index][0]=x;}
voidsetY(intindex,inty){coords[index][1]=y;}
HTetrisPieceShapepieceShape;
intcoords[4][2];
};
#endif//HTetrisPIECE_H
/*
*Filename:
HTetrispiece.cpp
*Author:
HaleChan
*Date:
20XX-11-24
*/
#include"HTetrisPiece.h"
#include
staticconstintcoordsTable[8][4][2]={
{{0,0},{0,0},{0,0},{0,0}},
{{0,-1},{0,0},{0,1},{0,2}},
{{-1,0}