该项目为一款基于QT的方块游戏项目。该项目与传统的俄罗斯方块游戏不同的是,在实现了传统方块游戏满行消除、多行消除、以及分值统计的基本需要。
首先,额外增添了技能项,当达到一定的条件,玩家便能够释放技能直接清除全屏方块;其次,额外设置了游戏难度自增,随着一定行数的消除,方块下坠速度会越来越快,增加游戏难度;最后,额外增添了排行榜,在游戏结束后,玩家的数据将会显示在排行榜之中。
本项目对QT中QGraphicsView类自定义,实现场景视图上的一切操作、并且响应boxGroup的消息、以及向主窗体传递消息。
对QWidget自定义,为程序的主窗体、显示一些窗口版块、以及对于来自view消息的响应。
对QPushButton进行自定义,实现一些鼠标进入、离开按钮的时间、音效、亮度变化的效果。
对QGraphicsItemGroup类自定义,实现几种不同的方块组形状及颜色的初始化、接收键盘消息,以完成俄罗斯方块对键盘的各种操作进行响应、使用计时器开始和暂停游戏、完成对场景坐标的变换,实现俄罗斯方块的翻转、碰撞检测。
对QGraphicsObject类自定义,实现方块内的小格子、以及碰撞的检测。
使用QT内置的sqlite,并调用QSqlDatabase进行了数据库的操作。
使用PS技术对项目素材进行了加工。
实现游戏初始加载动画
实现用户注册、登录界面及功能(含数据库检验)
实现游戏主界面设计
实现历史记录排行榜
实现方块形状及颜色的初始化
实现方块通过按键旋转(包含侧边碰撞返回)
实现方块满行消除(包含底边碰撞返回)
实现游戏中途暂停、鼠标脱离窗口自动暂停
实现技能的全屏消除
实现游戏中不同场景背景音乐的切换
界面交互设计的原则
统一性原则
界面风格统一:
用相同风格的素材进行界面背景的实现,对界面各空间采取相容的色调的控件,不同各种颜色胡乱混搭
美观性原则
界面美观大方,不同的游戏元素缓解玩家的视觉疲劳
易用性原则
操作方式应贴合使用者的日常使用习惯,不能设计反人类的操作体验
如下图所示:
游戏初始加载界面:
游戏加载成功:
注册登录界面:
文本输入框、按钮、标题栏统一使用与背景图片相合的色调
登入成功后,排行显示界面:
游戏主界面:
程序启动画面的设计主要是使用了Qt中的QSplashScreen这个类,为这个类创建对象的时候使用setPixmap传入图片,并且设置该对象的大小尺寸。
使用showMessage在图片上添加对应的文字,比如游戏规则。
接着调用show()方法即可显示。
需要结束启动画面时,则调用finish()方法,该方法的用来指定窗口初始化完成后结束启动画面。
部分代码如下:
QSplashScreen*splash=newQSplashScreen;
splash->setPixmap(QPixmap("路径"));
splash->setStyleSheet("font-size:16px;font-weight:480");
splash->showMessage(QObject::tr("…"),Qt::AlignCenter,QColor(255,255,255,255));
splash->show();
QThread::sleep(4);
splash->showMessage(QObject::tr(" \t游戏加载成功,正在进入..."),Qt::AlignCenter,QColor(255,255,255,255));
splash->finish(&w); //当主窗口启动后,启动画面隐藏
deletesplash;
初始化数据库,调用QSqlDatabase,进行数据库打开操作。建立一张新表,其中该标的属性列如下:
其中username为用户名(设为主键),password为密码,level为等级,maxsocore为最高分,allscore为玩家所进行游戏总分之和。(并为其设置5个初始数据)
对QWidget进行自定义类,其中setLoginWidget进行相关绘制,调用QLineEdit、QLabel、QPushButton实现注册登录输入窗口的各个部件,并设置其样式表。对按钮操作分别进行槽函数信号机制连接。
部分代码如下:
QSqlQueryquery(base);
query.exec("select*fromusertable");
while(query.next())
{if(query.value(0).toString()==userLine->text())
{if(query.value(1).toString()==passwordLine->text())
{query.prepare("select*fromusertablewhereusername=?");
query.addBindValue(userLine->text());
query.exec();
return;}
else{
label.setText(tr(" 用户名或密码有误,请重新输入"));
box.exec();
return;}
}
}
label.setText(tr(" 用户名或密码有误,请重新输入!"));
box.exec();
游戏主界面主要划分为以下三个区域:
提示区 游戏区 信息区
提示区界面主要运用了QLabel和QPusgButton等界面控件,进行样式表的绘制,而这些↑、↓、←、→、W、S、A、D皆为QPusgButton,因为要实现事件过滤器、根据按键操作提示窗口上的按钮会显示不同颜色,使得一旦出发键盘事件,对应按键的那些按钮实现颜色由绿变红,之后又还原为绿色。
游戏区在绘制过程3.5中有具体的讲解,此处便不多做赘述。
信息区上部分用来预示下一个方块组的形状,而下部方为分数、能量信息区域。因每当进行了满行消除操作时,下移操作申请新的方块时,会emit一个needNextGroup的信号,当接收到信号时调用createBoxGroup,然后将这个新生成的方块组存入Group中,并放置在QpintF的位置上。而分数栏则在使用setHtml进行设计,当满行消除时会调用一个updateScore函数,通过消除行数对gameEnergy、gameEnergy进行分别的累加后,直接该函数内新型样式变换,并通过调用QPropertyAnimation类进行分数动画效果。
排行榜数据皆为从数据库中选取载入模型,因此需先对数据库进行操作,然后加载到view之中。对QSqlQueryModel进行自定义类,因为QSqlQueryModel模型默认是只读的,所以我们在窗口上并不能对表格中的内容进行修改,然后按照我们自己的需要来显示数据和修改数据,因此对headerData、data进行重写。
使用QTableView、QHeaderView、QLabel制作出排行榜界面呈现为有序表格,因之前重写过data,表中数据从只读变为可读可写,调用Query写出数据库查询语句将结果写入。
效果如图:
使用QRectF设置一个float精度的矩形框边界,即游戏区域绘制。
对QGraphicsItemGroup类进行自定义,先设计一个长宽皆为20px的小方块,利用Qpaint对其贴图,使用画笔勾勒线型画刷填充,使其更具立体感。通过一个for循环将4个小方块组成一个items,并将其加入list列表,其中方块组有直型、J型、L型、Z型、S型、T型、田字型。
利用QTransform类,实现二维坐标系之间的转换。而碰撞检测非常重要,其主要在下面几个地方用到:在对屏幕中出现的方块组进行左右或者向下移动时,程序时先试探性的向左右或向下移动,如果发现有碰撞表示移动不成功,这时候就会自动按照刚刚的逆方向移动回去。另外在游戏结束判断时,如果方块组一开始出现的时候就发生了碰撞功能,说明游戏已经结束了。而碰撞函数功能的实现是采用item这个类自带的colldingItems函数来实现的,直接调用。
而移动则通过moveBy函数实现,旋转rotate函数实现。实质上在侧边时方块组进行旋转时,会先调用rotate,然后调用colldingItems函数并对其判断,测试到碰撞后,通过setTranform坐标逆向变形。
而自动下坠,使用了QTimer定时器,构造一个响应定时器的移步函数neesOneStep。当定时器时间到了,小方块组就向下移一步。
常规的满行消除的判断,是在给定的游戏区域每一个方块行进行检测,如果该行的list.count达到了10,则消行直接调用Qt库函数,而且通多创建模糊效果对象,调用animation函数对相关参数进行设定,实现动画效果。
同时,调用moveLines函数,将该行上面所有的方块都向下移一格,调用createBoxGroup函数出现下一个方块组进行游戏。
而全屏消除则需要接收技能键的消息,判断gameEnergy是否大于50,大于则减去50并isAllowJ函数返回true;此时则调用clearAll函数消除满行方块,和单行不同的是此时则需将list.count改为>=0即可实现。
部分代码如下:
for(inty=429;y>50;y=y-20)
{ QList<QGraphicsItem*>list=scene()->items(QRectF(299,y,202,22),Qt::ContainsItemShape,Qt::DescendingOrder);
if(list.count()==10)
{foreach(QGraphicsItem*item,list)
{group->waring=true;
oneBox*box=(oneBox*)item;
QGraphicsBlurEffect*effect=newQGraphicsBlurEffect;
box->setGraphicsEffect(effect);
QPropertyAnimation*animation=newQPropertyAnimation(box,"scale");
animation->setEasingCurve(QEasingCurve::OutBounce);
animation->setDuration(250);
animation->setStartValue(4);
animation->setEndValue(0.25);
animation->start(QAbstractAnimation::DeleteWhenStopped);
connect(animation,SIGNAL(finished()),box,SLOT(deleteLater()));
}rows<
if(rows.count()>0)
{QTimer::singleShot(400,this,SLOT(moveLines()));}
else
{ group->createBoxGroup(QPointF(400,70),nextGroup->getCurrentBox());
nextGroup->clearBoxGroup(true);
nextGroup->createBoxGroup(QPointF(700,110)); }
}
玩家自主暂停主要通过键盘事件实现,当检测到用户按下Z键时,emit一个信号给主窗体,主窗体接收到信号后会调用receiveResendPressZ函数对isActive进行判断,如果为真则说明窗体活动,则将暂停画面的图片show(显示)出来,同时给isActive设为false,并发出游戏暂停消息。
当玩家再按Z键时,即结束暂停回到游戏,因之前isActive设为false,因此会进入条件if语句的另一个分支,将图片hide(隐藏)起来,同时发出回到游戏消息。
部分代码如下:
voidmyWidget::receiveResendPressZ()
{
//暂停Z键的实现、如果是活动的、则暂停、否则重新开始游戏
if(isActive)
{
test->show();
isActive=false;
w->isGame=false;
player->pause();
emitpause();
return;
}
else
{
test->hide();
isActive=true;
view->setFocus();
w->isGame=true;
player->play();
emitrepause();
return;
}
}
为实现一些鼠标进入、离开按钮的事件、音效变化的效果。对QPushButton进行自定义,调用QtMultimedia和QMediaPlayer类,构造enterEvent函数,通过setMedia调用了音效资源,play方法进行播放。leaveEvent则通过stop方法便是的鼠标离开按钮停止播放。
而进入游戏主界面,及相对应的游戏结束则都直接调用QT库的类函数即可。
部分代码如下:
player->setPlaylist(playList);
playList->clear();
playList->addMedia(QUrl("路径"));
playList->setCurrentIndex(0);
playList->setPlaybackMode(QMediaPlaylist::CurrentItemInLoop);
player->setVolume(80);player->play();
由于在界面设计贴过游戏加载画面,在此处便不再进行贴图。
当用户注册不符合规范时:
当用户名已在数据库中重名时:
当用户注册合规时:
数据库中查看:已插入成功
当用户登录时输入不合规时:
当登录已注册过的用户时,直接进入开始游戏画面:
因还没进行游戏,该名玩家数据尚未进入排行榜,但是之前已经进行过游戏的玩家记录在此处显示出来。
点击开始游戏按钮:
满行消除:
因为消除了一行,分数此时变为了10,能量值也变为了2。此时上方已经出现新的方块组。
在进行一段时间游戏后,消除了多行方块,此时能量值已为50,可进行技能释放:
按下J键,释放技能,此时能量值减为0,同时全屏方块被清除,同时加上100分:
按下Z键,或者鼠标移出画面是自动暂停:
当游戏结束时,方块组超过边框时,则游戏结束:
此时记录已经进入了排行榜
设计方块组时:
考虑到碰撞检测,在对屏幕中出现的方块组进行左右或者向下移动时,程序其实还是会先进行移动,这个时候再通过碰撞检测函数判断移动是否成功,不成功的话则会自动按照刚刚的逆向移动回去。
而满行消除方块组,实质是移除方块组的小方块,调用的是QGraphicsItemGroup中的removeFromGroup函数。
设计游戏动态效果设置时:
当一行的小方块达到了满行时,在最初设计时直接删掉方块组,根本看不到任何动态效果。想要有一定的效果,可以通过调用QT库中的类,设置使删掉的小方块进行先后的坐标移动,给定的模糊效果,相结合达到改行爆炸的动态效果。
设计mylabel类时:
因普通的labe1锯齿感会比较严重,子类化Qlabel启用其反锯齿参数理应会减少锯齿,但是效果并不明显。
设置音效时:
一开始没使用QMediaPlaylist,这样的话音效只会播放一遍后就停止,而使用之后就能循环播放,似乎也是一个新的特性。