基于Qt5的方块游戏

1.项目简介

1.1项目说明:

该项目为一款基于QT的方块游戏项目。该项目与传统的俄罗斯方块游戏不同的是,在实现了传统方块游戏满行消除、多行消除、以及分值统计的基本需要。

首先,额外增添了技能项,当达到一定的条件,玩家便能够释放技能直接清除全屏方块;其次,额外设置了游戏难度自增,随着一定行数的消除,方块下坠速度会越来越快,增加游戏难度;最后,额外增添了排行榜,在游戏结束后,玩家的数据将会显示在排行榜之中。

1.2项目技术:

本项目对QT中QGraphicsView类自定义,实现场景视图上的一切操作、并且响应boxGroup的消息、以及向主窗体传递消息。

对QWidget自定义,为程序的主窗体、显示一些窗口版块、以及对于来自view消息的响应。

对QPushButton进行自定义,实现一些鼠标进入、离开按钮的时间、音效、亮度变化的效果。

对QGraphicsItemGroup类自定义,实现几种不同的方块组形状及颜色的初始化、接收键盘消息,以完成俄罗斯方块对键盘的各种操作进行响应、使用计时器开始和暂停游戏、完成对场景坐标的变换,实现俄罗斯方块的翻转、碰撞检测。

对QGraphicsObject类自定义,实现方块内的小格子、以及碰撞的检测。

使用QT内置的sqlite,并调用QSqlDatabase进行了数据库的操作。

使用PS技术对项目素材进行了加工。

 

2.需求分析及界面设计

2.1需求分析

实现游戏初始加载动画

实现用户注册、登录界面及功能(含数据库检验)

实现游戏主界面设计

实现历史记录排行榜

实现方块形状及颜色的初始化

实现方块通过按键旋转(包含侧边碰撞返回)

实现方块满行消除(包含底边碰撞返回)

实现游戏中途暂停、鼠标脱离窗口自动暂停

实现技能的全屏消除

实现游戏中不同场景背景音乐的切换

2.2界面设计

界面交互设计的原则

统一性原则

界面风格统一:
用相同风格的素材进行界面背景的实现,对界面各空间采取相容的色调的控件,不同各种颜色胡乱混搭

 

美观性原则

界面美观大方,不同的游戏元素缓解玩家的视觉疲劳

 

易用性原则

操作方式应贴合使用者的日常使用习惯,不能设计反人类的操作体验

 

 

如下图所示:

游戏初始加载界面:

 

 

 

 

 

 

 

 

 

 

游戏加载成功:

 

 

 

 

注册登录界面:

文本输入框、按钮、标题栏统一使用与背景图片相合的色调

 

 

 

登入成功后,排行显示界面:

 

 

 

 

游戏主界面:

3.概要设计

3.1实现游戏初始加载动画

程序启动画面的设计主要是使用了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;

 

 

 

 

 

 

 

 

 

 

 

 

 

3.2.实现用户注册、登录界面及功能(含数据库检验)

初始化数据库,调用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();

 

 

 

 

3.3实现游戏主界面设计

游戏主界面主要划分为以下三个区域:

  

提示区                   游戏区                      信息区

 

提示区界面主要运用了QLabelQPusgButton等界面控件,进行样式表的绘制,而这些、↓、←、→、W、S、A、D皆为QPusgButton,因为要实现事件过滤器、根据按键操作提示窗口上的按钮会显示不同颜色,使得一旦出发键盘事件,对应按键的那些按钮实现颜色由绿变红,之后又还原为绿色。

 

游戏区在绘制过程3.5中有具体的讲解,此处便不多做赘述。

 

信息区上部分用来预示下一个方块组的形状,而下部方为分数、能量信息区域。因每当进行了满行消除操作时,下移操作申请新的方块时,会emit一个needNextGroup的信号,当接收到信号时调用createBoxGroup,然后将这个新生成的方块组存入Group中,并放置在QpintF的位置上。而分数栏则在使用setHtml进行设计,当满行消除时会调用一个updateScore函数,通过消除行数对gameEnergygameEnergy进行分别的累加后,直接该函数内新型样式变换,并通过调用QPropertyAnimation类进行分数动画效果。

 

 

 

 

 

 

 

 

3.4实现历史记录排行榜 (含数据库操作)

排行榜数据皆为从数据库中选取载入模型,因此需先对数据库进行操作,然后加载到view之中。对QSqlQueryModel进行自定义类,因为QSqlQueryModel模型默认是只读的,所以我们在窗口上并不能对表格中的内容进行修改,然后按照我们自己的需要来显示数据和修改数据,因此对headerData、data进行重写。

使用QTableViewQHeaderViewQLabel制作出排行榜界面呈现为有序表格,因之前重写过data,表中数据从只读变为可读可写,调用Query写出数据库查询语句将结果写入。

效果如图:

 

 

 

 

 

 

 

 

 

3.5.实现游戏区绘制及方块组绘制

  使用QRectF设置一个float精度的矩形框边界,即游戏区域绘制。

QGraphicsItemGroup类进行自定义,先设计一个长宽皆为20px的小方块,利用Qpaint对其贴图,使用画笔勾勒线型画刷填充,使其更具立体感。通过一个for循环将4个小方块组成一个items,并将其加入list列表,其中方块组有直型、J型、L型、Z型、S型、T型、田字型。

 

 

 

 

 

 

 

3.6实现方块旋转、移动、自动下坠(包含侧边碰撞返回)

利用QTransform类,实现二维坐标系之间的转换。而碰撞检测非常重要,其主要在下面几个地方用到:在对屏幕中出现的方块组进行左右或者向下移动时,程序时先试探性的向左右或向下移动,如果发现有碰撞表示移动不成功,这时候就会自动按照刚刚的逆方向移动回去。另外在游戏结束判断时,如果方块组一开始出现的时候就发生了碰撞功能,说明游戏已经结束了。而碰撞函数功能的实现是采用item这个类自带的colldingItems函数来实现的,直接调用。

而移动则通过moveBy函数实现,旋转rotate函数实现。实质上在侧边时方块组进行旋转时,会先调用rotate,然后调用colldingItems函数并对其判断,测试到碰撞后,通过setTranform坐标逆向变形。

而自动下坠,使用了QTimer定时器,构造一个响应定时器的移步函数neesOneStep。当定时器时间到了,小方块组就向下移一步。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3.7实现方块满行消除及技能的全屏消除

    常规的满行消除的判断,是在给定的游戏区域每一个方块行进行检测,如果该行的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));  }

}

 

 

 

 

3.8实现游戏中途暂停

   玩家自主暂停主要通过键盘事件实现,当检测到用户按下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;

    }

}

 

 

 

 

 

 

3.9实现游戏中不同场景音效的切换

   为实现一些鼠标进入、离开按钮的事件、音效变化的效果。对QPushButton进行自定义,调用QtMultimediaQMediaPlayer类,构造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();

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

4.功能测试

由于在界面设计贴过游戏加载画面,在此处便不再进行贴图。

 4.1用户注册功能测试

当用户注册不符合规范时:

当用户名已在数据库中重名时:

 

当用户注册合规时:

 

数据库中查看:已插入成功

 

 

 

 

 

 

 

 

 

4.2用户登录功能测试

当用户登录时输入不合规时:

当登录已注册过的用户时,直接进入开始游戏画面:

 

因还没进行游戏,该名玩家数据尚未进入排行榜,但是之前已经进行过游戏的玩家记录在此处显示出来。

 

 

 

 

 

 

 

4.3游戏功能测试(满行消除、技能消除、暂停、游戏结束)

点击开始游戏按钮:

 

 

满行消除:

 

 

 

因为消除了一行,分数此时变为了10,能量值也变为了2。此时上方已经出现新的方块组。

 

 

 

在进行一段时间游戏后,消除了多行方块,此时能量值已为50,可进行技能释放:

 

按下J键,释放技能,此时能量值减为0,同时全屏方块被清除,同时加上100分:

 

 

 

 

 

按下Z键,或者鼠标移出画面是自动暂停:

 

当游戏结束时,方块组超过边框时,则游戏结束:

 

 

此时记录已经进入了排行榜

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

5.调试与分析

设计方块组时:

考虑到碰撞检测,在对屏幕中出现的方块组进行左右或者向下移动时,程序其实还是会先进行移动,这个时候再通过碰撞检测函数判断移动是否成功,不成功的话则会自动按照刚刚的逆向移动回去。

而满行消除方块组,实质是移除方块组的小方块,调用的是QGraphicsItemGroup中的removeFromGroup函数。

 

设计游戏动态效果设置时:

当一行的小方块达到了满行时,在最初设计时直接删掉方块组,根本看不到任何动态效果。想要有一定的效果,可以通过调用QT库中的类,设置使删掉的小方块进行先后的坐标移动,给定的模糊效果,相结合达到改行爆炸的动态效果。

 

设计mylabel类时:

  因普通的labe1锯齿感会比较严重,子类化Qlabel启用其反锯齿参数理应会减少锯齿,但是效果并不明显。

 

设置音效时:

  一开始没使用QMediaPlaylist,这样的话音效只会播放一遍后就停止,而使用之后就能循环播放,似乎也是一个新的特性。


你可能感兴趣的:(基于Qt5的方块游戏)