QT版连连看制作的实验报告.docx
《QT版连连看制作的实验报告.docx》由会员分享,可在线阅读,更多相关《QT版连连看制作的实验报告.docx(25页珍藏版)》请在冰豆网上搜索。
QT版连连看制作的实验报告
(此文档为word格式,下载后您可任意编辑修改!
)
一.游戏概述
1.1游戏简介
游戏“连连看”顾名思义就是找出相关联的东西,这个连连看在网上基本是用在小游戏中,就是找出相同的两样东西,在一定的规则之内可以做为相关联处理。
“连连看”的发展经历了从桌面游戏、在线游戏、社交游戏三个过程。
游戏“连连看”是源自台湾的桌面小游戏,自从流入大陆以来风靡一时,也吸引众多程序员开发出多种版本的“连连看”。
这其中,顾方编写的“阿达连连看”以其精良的制作广受好评,这也成为顾方“阿达系列软件”的核心产品。
并于2004年,取得了国家版权局的计算机软件著作权登记证书。
随着Flash应用的流行,网上出现了多种在线Flash版本“连连看”。
如“水晶连连看”,“果蔬连连看”等,流行的“水晶连连看”以华丽界面吸引了一大批的女性玩家。
2008年,随着社交网络的普及和开放平台的兴起,“连连看”被引入了社交网络。
“连连看”与个人空间相结合,被快速的传播,成为一款热门的社交游戏,其中以开发者Jonevey在Manyou开放平台上推出的“宠物连连看”最为流行。
1.2游戏功能
本次设计连连看主要是在原连连看对战的基础上设计挑战模式,具体的功能如下所述:
●游戏运行界面简单美观,操作简单,运行稳定;
●能够根据不同的等级模式播放不同的游戏背景音乐,包括操作方向键、消行时的声音,音量由玩家操作控制;
●设计不同的游戏难度,当玩家达到一定的积分,可以进入相关难度的等级;
玩家可直接操作界面上的相关按钮来控制游戏的开始、暂停、设置、退出等功能。
二.需求分析
2.1开发的需求
2.1.1C++
CC++的哲学就不一样了,同一份程序代码,只要在该平台重新Compile成该平台的执行档,就能在该平台执行,若文字模式下的程序,的确可以跨平台,程序完全不需修改,但GUI盛行以后,由于各GUI的Library都不一样且没有标准,所以GUI程序并没办法跨平台Compile,因为各平台都有专属的Library。
但是CC++优势就是执行速度快,若能解决GUILibrary的问题,则跨平台即可解决。
除了GUILibrary需要统一外,还得看该Library设计的好不好才行,LinuxGUI我没写过就不说了,若在Windows平台,Borland的C++Builder使用Delphi的VCLLibrary,Microsoft的VisualC++可以用MFC和.NETFramework,首先来看Borland的C++Builder,它使用标准的ANSIC++语言开发,利用VCL写出来的程序也算蛮漂亮的,执行速度很快,比较麻烦的是C++Builder当初为了缩短上市时间,并没有设计自己的Library,而是藉用Delphi的Library,导致C++Builder在Compile时,是先用Delphi的Compiler将VCLcompile成objectfile后,再用C++Builder将你写的C++程序compile成objectfile,最后再用Linker将所有的objectfilelink起来,由于这都是IDE在做,对程序设计师似乎影响不大,但VCL由于是Delphi的Library,所以若想深入研究VCL的sourcecode时,则必须学Delphi的Pascal才行,否则看不懂VCLsourcecode,这也是很多人对C++Builder却步的原因。
再来看Microsoft的VisualC++,MFC用的是完整的ANSIC++语言,所以语言部分不是问题,但若你看过用MFC写的程序,你一定会惊讶为什么这么复杂,程序代码非常冗长不好看,且MFC是有名的难学,若VisualC++搭配.NETFramrwork,则无法使用ANSIC++,得用其.NET平台版本的C++CLI,相信这点很多人就有意见了,不过.NETFramework由于设计的不错,所以写出来的程序相当精简漂亮,感觉和C++Builder差不多,不过无论是C++Builder或VisualC++开发的GUI,所开发出来的程序都无法跨平台,当然也无法跨平台Compile。
2.1.2QT
(1)QT的介绍
Qt是一个多平台的C++图形用户界面应用程序框架。
它提供给应用程序开发者建立艺术级的图形用户界面所需的所用功能。
Qt是完全面向对象的很容易扩展,并且允许真正地组件编程。
自从1996年早些时候,Qt进入商业领域,它已经成为全世界范围内数千种成功的应用程序的基础。
Qt也是流行的Linux桌面环境KDE的基础,KDE是所有主要的Linux发行版的一个标准组件。
Qt的特点有:
可移植性、易用性、执行速度快等特点。
(2)QT的优势
●XML支持。
●大量的开发文档
●支持2D3D图形渲染,支持OpenGL
●优良的跨平台特性,Qt支持下列操作系统:
MicrosoftWindows9598,MicrosoftWindowsNT,Linux,Solaris,SunOS,HP-UX,DigitalUNIX(OSF1,Tru64),Irix,FreeBSD,BSDOS,SCO,AIX,OS390,QNX等等。
●面向对象,Qt的良好封装机制使得Qt的模块化程度非常高,可重用性较好,对于用户开发来说是非常方便的。
Qt提供了一种称为signalsslots的安全类型来替代callback,这使得各个元件之间的协同工作变得十分简单。
●丰富的API,Qt包括多达250个以上的C++类,还提供基于模板的collections,serialization,file,IOdevice,directorymanagement,datetime类。
甚至还包括正则表达式的处理功能。
2.2游戏功能需求
本次设计是在传统的连连看上设计实现对战的连连看,玩家通过达到一定的积分来获得继续闯关的机会,还有机会开启神秘的应藏关,以此来增加游戏的可玩性。
游戏的基本规则:
程序随机产生任意成对的图片,当定点击开始游戏时,电脑根据时间参数随机生成固定的成对图片,游戏再次开始,玩家可通过鼠标上的左键,自由的点两张图片。
如果点击两次时,两张图片相同,且满足程序的算法则这两张图片可消去。
消去图片后,游戏可给玩家加分,若在规定的时间内,玩家没有消掉所有的图片则游戏失败。
由于是闯关模式,达到一定分数的玩家可以继续挑战新的游戏,游戏的难度会相应增加,并且还增加了新玩法。
具体的游戏功能如下:
游戏界面需求:
设计良好的游戏界面可以让玩家充分感受到游戏带来的娱乐性,游戏的背景取自网上中的图片,体现了游戏的挑战性。
鼠标处理事件需求:
通过点击主窗体中相应的按钮,可以实现游戏的开始、暂停、结束,通过点击选项设置中相应的按钮,可设置声音的大小及方向键↓的功能。
显示需求:
当两次点击的图片相同且满足算法可以消去,当达到一定分数的时候,游戏会进入下一关,并有障碍了。
游戏闯关设计需求:
随着游戏的难度不同,玩法会发生改变,需要在游戏中设置障碍,蒙手游戏。
如果玩家能到达并完成最后一关,则玩家挑战成功,游戏结束。
最后游戏效果如下:
三.游戏总体设计
3.1总体设计
整体设计思想:
进入游戏后,有三个按钮可供玩家选择:
开始游戏,游戏设置,退出游戏,在进入相应子菜单后也可返回到主菜单,每个菜单的具体设计将在后面介绍,以下是总体的游戏流程图。
3.2游戏核心模块的设计
3.2.1连连看所要求的是:
1.两个目标是相同的;
2.两个目标之间连接线的折点不超过两个。
(连接线由x轴和y轴的平行线组成)那么分析一下连接的情况可以看到,一般分三种情况:
(1)直线相连;
(2)一个折点;(3)两个折点。
可以发现,如果有折点,每个折点必定有且至少有一个坐标(x或者y)是和其中一个目标点是相同的,也就是说,折点必定在两个目标点所在的x方向或y方向的直线上。
所以设计思路就是:
假设目标点p1,p2,如果有两个折点分别为z1,z2那么,所要进行的是:
Ø如果验证p1,p2直线连线,则连接成立
Ø搜索以p1,p2的x,y方向四条直线(可能某两条直线会重合)上的有限点,每次取两点作为z1,z2,验证p1到z1z1到z2z2到p2是否都能直线相连,是则连接成立。
3.2.2连连看消去算法实现
在检验两张图片能否消掉的时候,我们要让两张图片同时满足两个条件才行,就是两者配对并且连线成功。
分3种情况:
(从下面的这三种情况,我们可以知道,需要三个检测,这三个检测分别检测一条直路经。
这样就会有三条路经。
若这三条路经上都是空按钮,那么就刚好是三种直线(两个转弯点)把两个按钮连接起来了)
(1)相邻
(2)若不相邻的先在第一个按钮的同行找一个空按钮。
1).找到后看第二个按钮横向到这个空按钮所在的列是否有按钮。
2).没有的话再看第一个按钮到与它同行的那个空按钮之间是否有按钮。
3).没有的话,再从与第一个按钮同行的那个空按钮竖向到与第二个按钮的同行看是否有按钮。
没有的话路经就通了,可以消了.
(3)若2失败后,再在第一个按钮的同列找一个空按钮。
1).找到后看第二个按钮竖向到这个空按钮所在的行是否有按钮2).没有的话,再看第一个按钮到与它同列的那个空按钮之间是否有按钮。
3).没有的话,再从与第一个按钮同列的那个空按钮横向到与第二个按钮同列看是否有按钮。
没有的话路经就通了,可以消了。
若以上三步都失败,说明这两个按钮不可以消去。
四.具体方案
4.1视图层设计
4.1.1图形的产生
加载图块图片资源,调用图片库用函数DrawArea:
:
loadPixmap()来实现。
函数如下设计:
voidDrawArea:
:
loadPixmap()
{
background.load(":
backgroundbackground.png");
background=background.scaled(size());
QPixmappix(":
patternpattern.png");
intnum=pix.true;
for(i=0;ifor(j=0;jif(board[i][j]){
for(x=i;xif(x==i)y=j+1;
elsey=0;
for(;yif(board[x][y]==board[i][j]){
if(isPosLinkable(i,j,x,y)){如果消块成功,那么这里就变成了0
false;
}
}
}
}
}
}
}
returntrue;
}
4.3其它相关技术的实现
键盘事件响应
连连看是通过鼠标左键来控制游戏的运行,那么是具体如何实现的呢?
这就需要用到键盘的左击事件,通过响应鼠标的按下事件来实现。
玩家通过鼠标左键来控制图片的点击。
具体代码实现:
通过重新实现虚函数GameWindow:
:
keyPressEvent(QKeyEvent*event)来响应相应的键盘按键事件。
五.游戏的测试
5.1选项按钮的功能测试
(1)点击“游戏开始”:
游戏正常开始,结果正常;
(2)点击“退出游戏”:
游戏正常退出,结果正常;
(3)点击“暂停”:
游戏停止,“暂停”变为“取消暂停”,再点击“取消暂停”:
游戏继续,结果正常;
(4)点击“返回主菜单”:
游戏退回到主菜单界面,结果正常;
5.2按键事件的功能测试
(1)两次点击相同图片,若满足算法,图片消失,则结果正常;
(2)图片点击测试:
游戏过程中,点击鼠标左键:
图片在没有其他图片阻挡的情况下可变色,结果正常;
5.3图片消失测试
(1)当点击两张相同图片:
两站图片同时消失,结果正常;
(2)当图片消失,且满足一条直线时分数增加10分。
5.4声音和显示测试
(1)进入游戏时:
背景音乐正常播放,图片消失时,有背景音乐播放;
(2)在游戏过程中,游戏区域背景颜色不断改变;在开始最后一关时:
游戏区域越来越大,结果正常;
5.5测试结果分析
经过测试,本游戏实现了基本的连连看的功能,运行比较稳定,不过有些细节方面可能需要改进,游戏在很多方面还需要进一步完善。
六.小结
游戏设计与实践是一项复杂而庞大的工作,在选题之后,我才意识到过程的艰难,因为以前从来都没有接触过游戏设计,刚开始有点迷茫和彷徨。
后来通过翻阅书籍和在网上查阅资料,逐渐找到了一些感觉。
本次设计让我初步懂得了电子游戏涉及到的有关技术、方法,包括电子游戏选题、构思、设计步骤等。
并实现一些可演示的游戏软件,其中有很多应用了学习的相关技术,并且做到了界面、声音都能实际演示。
此次设计过程中印象最深的收获有:
1、学到了很多新知识,并且对老知识进行了回顾。
经过长时间的学习,更进一步熟悉了Qt编程、通过不断上机实验,调试程序,总结经验,从对课题的不理解到能够开始动手去做,提出新问题并自己想办法去解决问题,自己多实践,所以增强了动手能力。
2、提高了中、英文资料的检索能力。
这次专业设计过程中我查阅了多资料,包括一些期刊、杂志,还有网络中的电子文档、电子书籍、网页及下载的视频教学课程;不但有中文资料还有英文资料。
这些资料,使我的眼界更开阔,对课题的认识更加深刻,编写程序的时候思路更加清楚,少走了很多弯路。
回顾此次设计过程,我学到了许多书本上没有学到的知识。
通过这次自己制作的软件,丰富了自己的实践技能,扩张了本专业的知识面,使我受益匪浅,同时也体验到了搞软件开发的难度。
在这次设计的同时,由于我对这样的软件开发还只是一个开始,了解的不多,这其中或许还有很多的不足,有些模块做得不是很好,有些功能还不能够完全的实现,如播放背景音乐时,只能播放一遍,因为游戏Qt类库中封装的东西太多,有些函数它底层的具体实现可能还没有真正的理解,所以,这也许就是本次游戏设计的不足之处。
七.源码(部分代码):
*drawarea.cpp*
#include"drawarea.QSize(AREA_WIDTH,AREA_HEIGHT);
}
QSizeDrawArea:
:
sizeHint()const
{
returnsize();
}
*初始化关卡*
voidDrawArea:
:
initLevel(constMap&mapData)
{
lastX=mapData.xMax;
lastY=mapData.yMax;
lineTimer=0;
;
}
if(pairLeft==0){
emiterrorNotify(tr("地图数据为空"));
return;
}
pairLeft=pairLeft2;
disorder(mapData.disorderCount);
刚开局就判断死局,一个是初始化上帝之手,
再一个最主要原因是为hint图块对赋值
上帝之手的初始化必须在地图数据加载之后进行
if(isDead()){
godTouch=true;
emitgodTouchOn();
}else
godTouch=false;
isLevelOver=false;
drawBoard();
}
[Test]从文件加载地图及相关配置信息。
voidDrawArea:
:
loadMapFromFile(stringfileName)
{
intnDisorder;
FILE*mapFile;
mapFile=fopen(fileName.c_str(),"r");
if(!
mapFile)
exit(-1);
if(fscanf(mapFile,"%d%d",&nDisorder,&pairLeft)==EOF){
emiterrorNotify(tr("Test:
地图数据加载失败"));
return;
}
for(inti=0;ifor(intj=0;jif(fscanf(mapFile,"%d",&board[i][j])==EOF){
emiterrorNotify(tr("Test:
地图数据无法成功加载"));
return;
}
}
fclose(mapFile);
disorder(nDisorder);
}
根据指定的打乱次数nDisorder对面板内的非零数据进行交换
voidDrawArea:
:
disorder(intnDisorder)
{
srand(clock());
基本实现:
将非零数据加入到一个足够长的数组中,随机产生两个
坐标进行交换
intpos[xMax*yMax][2];
intnBlock=0;
for(inti=0;ifor(intj=0;jif(board[i][j]){
pos[nBlock][0]=i;
pos[nBlock][1]=j;
++nBlock;
}
}
for(inti=nDisorder;i;--i){
intpos1=rand()%nBlock;
generatePos2:
intpos2=rand()%nBlock;
if(pos1==pos2)
gotogeneratePos2;
intx1=pos[pos1][0];
inty1=pos[pos1][1];
intx2=pos[pos2][0];
inty2=pos[pos2][1];
std:
:
swap(board[x1][y1],board[x2][y2]);
}
drawBoard();
}
*加载图块图片资源*
voidDrawArea:
:
loadPixmap()
{
background.load(":
backgroundbackground.png");
background=background.scaled(size());
QPixmappix(":
patternpattern.png");
intnum=pix.pen(Qt:
:
SolidLine);
pen.setColor(QColor(220));
font.setFamily("ComicSansMS");
font.setPixelSize(30);
font.setBold(true);
painter.setPen(pen);
painter.setFont(font);
painter.drawText(QPointF(0,AREA_HEIGHT-15),tr("x%1连击!
").arg(multiHit));
}
}
painter.end();
drawArea->setPixmap(store);
if(isHintUsed){
drawEmbrace();
QPenpen;
pen.setWidth(5);
pen.setColor(Qt:
:
blue);
painter.setPen(pen);
painter.translate(y*PIX_SIZE,x*PIX_SIZE);
painter.drawRect(0,0,PIX_SIZE,PIX_SIZE);
painter.end();
drawArea->setPixmap(store);
}
}
*在指定的图块上画一个红色的叉号*
voidDrawArea:
:
drawRedMark(constint&x,constint&y)
{
QPainterpainter(&store);
QPenpen(Qt:
:
white);
pen.setWidth(5);
painter.setPen(pen);
painter.translate(y*PIX_SIZE,x*PIX_SIZE);
painter.drawLine(0,0,PIX_SIZE,PIX_SIZE);
painter.drawLine(0,PIX_SIZE,PIX_SIZE,0);
painter.end();
drawArea->setPixmap(store);
}
*在两个给定点之间画一条较粗的直线*
voidDrawArea:
:
drawLine(constint&aX,constint&aY,
constint&bX,constint&bY)
{
QPainterpainter(&store);
intx1=aY*PIX_SIZE+PIX_SIZE2;
inty1=aX*PIX_SIZE+PIX_SIZE2;
intx2=bY*PIX_SIZE+PIX_SIZE2;
inty2=bX*PIX_SIZE+PIX_SIZE2;
QPenpen(Qt:
:
blue);
pen.setStyle(Qt:
:
SolidLine);
pen.setWidth(5);
painter.setPen(pen);
painter.drawLine(x1,y1,x2,y2);
painter.end();
drawArea->setPixmap(store);
}
voidDrawArea:
:
autoDestroy()
{
lastX=true;
for(i=0;ifor(j=0;jif(board[i][j]){
for(x=i;xif(x==i)y=j+1;
elsey=0;
for(;yif(board[x][y]==board[i][j]){
if(isPosLinkable(i,j,x,y)){如果消块成功,那么这里就变成了0
false;
}
}
}
}
}
}
}
returntrue;
}
*在(x,y)位置发生了点击,如果上次点击的点花色与之相同且位置不同,
*对于上帝模式,直接销块。
*否则,尝试判断是否可连接,如果可连接则进行连线,
*并随后进行死局判断,如进入死局则开启上帝模式
*否则将这个点记为上次点击的点。
*每次成功销块之后,立即增加连击计数并启动一个连击定时器,如果连击
*定时器过期,则连击计数清零。
*
voidDrawArea:
:
clickPos(intx,inty)
{
assert(isValid(x,y));
assert(!
isBlank(x,y));
vectorposVec;
intnPos=0;
Pospos1,pos2;
if(lastX==x&&lastY==y)
return;
if(!
isValid(lastX,lastY)||
!
isSame(x,y,lastX,lastY)||isS