目錄
前言
一、游戲整體分析
二、創(chuàng)建項(xiàng)目
三、添加資源
四、主界面實(shí)現(xiàn)
1、設(shè)置游戲主場景基本配置
2、設(shè)置背景圖片
3、創(chuàng)建開始按鈕并設(shè)置動(dòng)畫
4、創(chuàng)建關(guān)卡選擇界面并實(shí)現(xiàn)主界面與其的切換
五、關(guān)卡選擇界面實(shí)現(xiàn)
1、設(shè)置關(guān)卡選擇場景基本配置
2、設(shè)置關(guān)卡選擇場景的背景圖片
3、返回按鈕的相關(guān)功能設(shè)置
4、創(chuàng)建選擇關(guān)卡按鈕
5、實(shí)現(xiàn)點(diǎn)擊關(guān)卡按鈕跳轉(zhuǎn)到對應(yīng)的翻金幣場景功能
六、翻金幣場景實(shí)現(xiàn)
1、設(shè)置游戲場景基本配置
2、設(shè)置游戲場景的背景圖片
3、設(shè)置返回按鈕及其功能
4、在左下角顯示當(dāng)前關(guān)卡號
5、創(chuàng)建游戲界面中間的金幣背景
6、創(chuàng)建金幣類
7、引入關(guān)卡數(shù)據(jù)
8、初始化各個(gè)關(guān)卡
9、實(shí)現(xiàn)翻金幣特效
10、實(shí)現(xiàn)周圍金幣跟著翻轉(zhuǎn)功能
11、判斷是否勝利
12、顯示勝利圖片
13、勝利后禁用按鈕
七、添加音效
八、優(yōu)化項(xiàng)目
1、切換三個(gè)場景的進(jìn)入與返回都在同一個(gè)位置下
2、在游戲勝利瞬間點(diǎn)了其他金幣使得未勝利(速度過快)
九、打包項(xiàng)目???????
前言
對QT的相關(guān)知識與控件進(jìn)行簡單的學(xué)習(xí)之后,通過實(shí)現(xiàn)“翻金幣游戲”來鞏固與實(shí)踐所學(xué)的QT知識。在制作過程中是根據(jù)以下視頻的教程進(jìn)行制作的。感興趣的可以移步視頻。
02 案例簡介_嗶哩嗶哩_bilibili
制作翻金幣游戲的圖片及音頻都是直接利用課程提供的的現(xiàn)有資料,如果在課程沒有找到的話,可以通過以下鏈接下載對應(yīng)的資料。
鏈接:https://pan.baidu.com/s/1IxWIa47V0L_WuLu41pu7Tw?
提取碼:lje1
本文將從頭到尾循序漸進(jìn)地對“CoinFlip”游戲的實(shí)現(xiàn)進(jìn)行介紹,會(huì)相對詳細(xì)一點(diǎn),主要從整體分析、項(xiàng)目創(chuàng)建、項(xiàng)目實(shí)現(xiàn)、項(xiàng)目打包這幾個(gè)方面進(jìn)行介紹。
打包好的游戲鏈接如下,可以直接下載后運(yùn)行就可以玩了。
鏈接:https://pan.baidu.com/s/1Nfzstm1AbsmwfKPL_ac4_Q?
提取碼:rqa6
添加自己創(chuàng)建的頭文件使用以下形式添加
#include "xxxx.h"
一、游戲整體分析
這個(gè)小游戲總的來看需要我們實(shí)現(xiàn)的有三個(gè)界面還有兩個(gè)自定義控件。
? ? ? ?一是游戲開始界面,對應(yīng)圖1;其中,開始界面需要一個(gè)開始按鈕,能夠進(jìn)行頁面切換,因而我們需要自定義一個(gè)控件,也就是MyPushButton,父類為QPushButton,進(jìn)而實(shí)現(xiàn)這一按鈕的設(shè)計(jì)與動(dòng)作實(shí)現(xiàn)。
? ? ? ? 二是關(guān)卡選擇界面,對應(yīng)圖2;其中,關(guān)卡按鈕還有返回按鈕back也是利用我們自定義的控件來實(shí)現(xiàn)。
? ? ? ? 三是關(guān)卡界面,對應(yīng)圖3。其中,因?yàn)橛螒蛞?guī)則是點(diǎn)擊銀幣然后上下左右的硬幣跟著翻轉(zhuǎn),需要利用多個(gè)圖片來實(shí)現(xiàn)金幣翻轉(zhuǎn),因此我們需要?jiǎng)?chuàng)建一個(gè)金幣類來實(shí)現(xiàn)金幣所需要進(jìn)行的對應(yīng)操作。
? ? ? ? 而圖4只是在玩家勝利之后在關(guān)卡界面的基礎(chǔ)上彈出成功圖片,因而不需要另外創(chuàng)建界面。三個(gè)界面就對應(yīng)三個(gè)類,分別是mainscene、chooselevelscene、playscene;自定義的類有兩個(gè),分別是mypushbutton、mycoin類。
二、創(chuàng)建項(xiàng)目
打開QT Creator(我用的是5.12.9版本)-->創(chuàng)建新項(xiàng)目-->選擇Qt Widgets Application-->下一步
設(shè)置項(xiàng)目名稱“CoinFlip”-->選擇存放路徑-->下一步
從主界面開始實(shí)現(xiàn),設(shè)置類名為“MainScene”-->選擇父類為“QMainWindow”-->勾選“Generate form”-->下一步
然后項(xiàng)目創(chuàng)建成功,項(xiàng)目結(jié)構(gòu)如下:
三、添加資源
將以下資源添加到項(xiàng)目中
將前言中準(zhǔn)備好的資料下載保存到電腦桌面-->點(diǎn)擊項(xiàng)目右鍵選擇“Add New”-->選擇“QT”-->選擇“QT Resource File”-->下一步-->設(shè)置名稱“res”-->下一步-->完成
點(diǎn)擊完成后會(huì)出現(xiàn)如下界面,點(diǎn)擊“Add Prefix”-->將前綴設(shè)置為“/”即可。
(test是為了方便截圖添加資源等操作,不用在意這個(gè))
然后將前面保存的資料里面的res文件夾,復(fù)制粘貼到CoinFlip項(xiàng)目中(也就是前面創(chuàng)建項(xiàng)目時(shí)你選擇的路徑)【忽略test,不是重點(diǎn)】
回到項(xiàng)目界面,點(diǎn)擊“Add Files”-->選擇剛才添加的"res"文件-->選中文件夾中所有文件-->點(diǎn)擊打開-->成功添加資源
添加成功后如下:
四、主界面實(shí)現(xiàn)
1、設(shè)置游戲主場景基本配置
首先我們利用ui界面設(shè)計(jì)來實(shí)現(xiàn)菜單欄的設(shè)計(jì)——雙擊“mainscene.ui”打開界面設(shè)計(jì)——界面如下
設(shè)計(jì)菜單欄——在中間上方“在這里輸入”這里輸入“開始”,然后點(diǎn)擊“開始”,在下方的“在這里輸入”,輸入“Quit”,然后再找到其text屬性將其改成退出。
【之所以先輸入Quit是因?yàn)榭丶Q是自動(dòng)生成的,總不能生成"action退出"】
然后移除自帶的狀態(tài)欄,然后就可以回到"mainscene.cpp",在構(gòu)造函數(shù)進(jìn)行代碼的編寫
實(shí)現(xiàn)場景的基本配置,設(shè)置窗口大小、應(yīng)用的圖標(biāo)、還有窗口的標(biāo)題,然后實(shí)現(xiàn)退出的功能實(shí)現(xiàn),利用信號和lambda表達(dá)式來實(shí)現(xiàn)
代碼如下:
//設(shè)置固定大小
this->setFixedSize(350,550);
//設(shè)置應(yīng)用圖片
this->setWindowIcon(QPixmap(":/res/Coin0001.png"));
//設(shè)置窗口標(biāo)題
this->setWindowTitle("一起來翻金幣吧");
//點(diǎn)擊退出,退出程序
connect(ui->actionQuit,&QAction::triggered,[=](){
this->close();
});
運(yùn)行界面結(jié)果如下:
2、設(shè)置背景圖片
需要重寫MainScene的PaintEvent事件,先在“mainscene.h”中添加聲明,然后在“mainscene.cpp”中實(shí)現(xiàn)背景圖片的添加。
【注:PaintEvent函數(shù)在項(xiàng)目運(yùn)行時(shí)會(huì)自動(dòng)調(diào)用生成,因此只需要重寫而不需要在cpp中調(diào)用】
代碼如下:
void MainScene::paintEvent(QPaintEvent *){
//創(chuàng)建畫家,制定繪圖設(shè)備
QPainter painter(this);
//創(chuàng)建QPixmap對象
QPixmap pix;
//加載圖片
pix.load(":/res/PlayLevelSceneBg.png");
//繪制背景圖
painter.drawPixmap(0,0,this->width(),this->height(),pix);
//加載標(biāo)題
pix.load(":/res/Title.png");
//縮放圖片
pix=pix.scaled(pix.width()*0.5,pix.height()*0.5);
//繪制標(biāo)題
painter.drawPixmap(10,30,pix.width(),pix.height(),pix);
}
運(yùn)行界面結(jié)果如下:
3、創(chuàng)建開始按鈕并設(shè)置動(dòng)畫
? ? ? 需要實(shí)現(xiàn)以下場景——開始按鈕點(diǎn)擊后有彈跳效果
這個(gè)效果我們利用自定義控件實(shí)現(xiàn)(因?yàn)镼PushButton不會(huì)自帶這類特效),我們可以自己封裝出一個(gè)按鈕控件,來實(shí)現(xiàn)這些效果。此時(shí)需要我們創(chuàng)建一個(gè)新的類——MyPushButton
? ? ?選擇項(xiàng)目右鍵,選擇“Add New”-->選擇“C++”-->選擇“C++ Class”-->下一步-->設(shè)置類名“MyPushButton”-->選擇父類“QWidget”-->下一步-->完成
創(chuàng)建成功后項(xiàng)目結(jié)構(gòu)如下:
分別打開“mypushbutton.h”和“mypushbutton.cpp”修改其繼承的父類為QPushButton
修改后如下:
我們希望點(diǎn)擊按鈕之后能夠看到按鈕的變化,即可以讓MyPushButton提供正常顯示的圖片以及按下后顯示的圖片,比如上下跳躍或者變成被按下的樣子,因此我們需要重寫MyPushButton類的構(gòu)造函數(shù),使得它能夠接受兩個(gè)參數(shù),分別是正常顯示的圖片的路徑以及按下后要顯示的圖片路徑。
先在mypushbutton.h文件中對重載的構(gòu)造函數(shù)進(jìn)行聲明以及定義兩個(gè)成員變量,分別用來存放對應(yīng)圖片的路徑。
代碼如下:
//normalImg 代表正常顯示的圖片
//pressImg 代表按下后顯示的圖片,默認(rèn)為空
MyPushButton(QString normalImg,QString pressImg = "");
private:
QString normalImgPath; //默認(rèn)顯示圖片路徑
QString pressedImgPath; //按下后顯示圖片路徑
然后就是在“mypushbutton.cpp”中重寫構(gòu)造函數(shù)的具體實(shí)現(xiàn)代碼
代碼如下:
MyPushButton::MyPushButton(QString normalImg,QString pressImg){
//成員變量normalImgPath保存正常顯示圖片路徑
normalImgPath=normalImg;
//成員變量pressedImgPath保存按下后顯示的圖片
pressedImgPath=pressImg;
//創(chuàng)建QPixmap對象
QPixmap pixmap;
//判斷是否能夠加載正常顯示的圖片,若不能提示加載失敗
bool ret=pixmap.load(normalImgPath);
if(!ret){
qDebug()<<normalImg<<"加載圖片失敗!";
}
//設(shè)置圖片的固定尺寸
this->setFixedSize(pixmap.width(),pixmap.height());
//設(shè)置不規(guī)則圖片的樣式表
this->setStyleSheet("QPushButton{border:0px;}");
//設(shè)置圖標(biāo)
this->setIcon(pixmap);
//設(shè)置圖標(biāo)大小
this->setIconSize(QSize(pixmap.width(),pixmap.height()));
}
前面兩步做好之后,我們就可以在主界面上添加開始按鈕,打開“mainscene.cpp”
代碼如下:
//利用自定義控件定義開始按鈕,同時(shí)設(shè)置其圖標(biāo) 這里就運(yùn)用到了重載的構(gòu)造函數(shù)
MyPushButton * startBtn = new MyPushButton(":/res/MenuSceneStartButton.png");
//將按鈕的父親設(shè)置為this,也就是添加到對象樹上,這樣當(dāng)窗口析構(gòu)時(shí)其也會(huì)被析構(gòu)
startBtn->setParent(this);
//設(shè)置開始按鈕在主界面的位置 也就是x,y
startBtn->move(this->width()*0.5-startBtn->width()*0.5,this->height()*0.7);
運(yùn)行界面結(jié)果如下:
此時(shí)的開始按鈕點(diǎn)擊沒有任何特效,因此我們需要添加點(diǎn)擊后上下跳躍的特效,我們定義兩個(gè)函數(shù)zoom1()和zoom2(),分別用來實(shí)現(xiàn)向下跳躍和向上跳躍,這兩個(gè)函數(shù)放在mypushbutton類中實(shí)現(xiàn)。
先在mypushbutton.h中添加聲明
在mypushbutton.cpp中添加實(shí)現(xiàn)代碼,兩個(gè)函數(shù)代碼基本一致,只有位置有不同
代碼如下:
//向下跳躍
void MyPushButton::zoom1()
{
//創(chuàng)建動(dòng)畫對象
QPropertyAnimation * animation1 = new QPropertyAnimation(this,"geometry");
//設(shè)置時(shí)間間隔,單位毫秒
animation1->setDuration(200);
//創(chuàng)建起始位置
animation1->setStartValue(QRect(this->x(),this->y(),this->width(),this->height()));
//創(chuàng)建結(jié)束位置
animation1->setEndValue(QRect(this->x(),this->y()+10,this->width(),this->height()));
//設(shè)置緩和曲線,QEasingCurve::OutBounce 為彈跳效果
animation1->setEasingCurve(QEasingCurve::OutBounce);
//開始執(zhí)行動(dòng)畫
animation1->start();
}
//向上跳躍
void MyPushButton::zoom2()
{
//創(chuàng)建動(dòng)畫對象
QPropertyAnimation * animation1 = new QPropertyAnimation(this,"geometry");
//設(shè)置時(shí)間間隔,單位毫秒
animation1->setDuration(200);
//創(chuàng)建起始位置
animation1->setStartValue(QRect(this->x(),this->y()+10,this->width(),this->height()));
//創(chuàng)建結(jié)束位置
animation1->setEndValue(QRect(this->x(),this->y(),this->width(),this->height()));
//設(shè)置緩和曲線,QEasingCurve::OutBounce 為彈跳效果
animation1->setEasingCurve(QEasingCurve::OutBounce);
//開始執(zhí)行動(dòng)畫
animation1->start();
}
然后到“mainscene.cpp”中連接信號槽,實(shí)現(xiàn)點(diǎn)擊開始按鈕后按鈕上下跳動(dòng)的效果
代碼如下:
//監(jiān)聽點(diǎn)擊事件,執(zhí)行特效
connect(startBtn,&MyPushButton::clicked,[=](){
startBtn->zoom1(); //向下跳躍
startBtn->zoom2(); //向上跳躍
});
此時(shí)點(diǎn)擊運(yùn)行項(xiàng)目,點(diǎn)擊開始按鈕就可以看到上下跳躍的效果。
【到這一步主場景的創(chuàng)建已經(jīng)基本實(shí)現(xiàn)了,接下來就是創(chuàng)建第二個(gè)界面——關(guān)卡選擇,同時(shí)實(shí)現(xiàn)點(diǎn)擊開始按鈕后跳轉(zhuǎn)到關(guān)卡選擇界面】
4、創(chuàng)建關(guān)卡選擇界面并實(shí)現(xiàn)主界面與其的切換
同創(chuàng)建自定義控件一樣,我們創(chuàng)建關(guān)卡選擇界面需要?jiǎng)?chuàng)建一個(gè)新的類——“ChooseLevelScene”
??選擇項(xiàng)目右鍵,選擇“Add New”-->選擇“C++”-->選擇“C++ Class”-->下一步-->設(shè)置類名“ChooseLevelScene”-->選擇父類“QMainWindow”-->下一步-->完成
創(chuàng)建成功后項(xiàng)目結(jié)構(gòu)如下:
然后就是實(shí)現(xiàn)點(diǎn)擊開始按鈕進(jìn)入選擇關(guān)卡場景的功能。因此我們需要在“mainscene.h”中添加關(guān)卡場景的對象,以此來實(shí)現(xiàn)在點(diǎn)擊按鈕之后,通過"chooseScene->show()"來實(shí)現(xiàn)界面切換。
代碼如下:
//選擇關(guān)卡場景
ChooseLevelScene *chooseScene = new ChooseLevelScene;
然后就是在“mainscene.cpp”中點(diǎn)擊開始按鈕后的lambda表達(dá)式中添加代碼實(shí)現(xiàn)界面切換。在調(diào)用zoom2()后面添加即可。
代碼如下:
//延時(shí)0.5秒后 進(jìn)入選擇場景
QTimer::singleShot(500, this,[=](){
this->hide();
chooseScene->show();
});
運(yùn)行程序,執(zhí)行特效后延時(shí)0.5秒進(jìn)入選擇關(guān)卡場景,結(jié)果如下:(因?yàn)殛P(guān)卡選擇界面還未進(jìn)行設(shè)置,因此是空白界面)
五、關(guān)卡選擇界面實(shí)現(xiàn)
1、設(shè)置關(guān)卡選擇場景基本配置
直接在“chooselevelscene.cpp”中的構(gòu)造函數(shù)中去設(shè)置界面的配置,如大小、菜單欄等等
代碼如下:
//設(shè)置窗口固定大小
this->setFixedSize(350,550);
//設(shè)置圖標(biāo)
this->setWindowIcon(QPixmap(":/res/Coin0001.png"));
//設(shè)置標(biāo)題
this->setWindowTitle("選擇關(guān)卡");
//創(chuàng)建菜單欄
QMenuBar * bar = this->menuBar();
this->setMenuBar(bar);
//創(chuàng)建開始菜單
QMenu * startMenu = bar->addMenu("開始");
//創(chuàng)建按鈕菜單項(xiàng)
QAction * quitAction = startMenu->addAction("退出");
//點(diǎn)擊退出 退出游戲
connect(quitAction,&QAction::triggered,[=](){this->close();});
此時(shí)運(yùn)行結(jié)果如下:
2、設(shè)置關(guān)卡選擇場景的背景圖片
跟主場景一樣,需要重寫paintEvent函數(shù),先在“chooselevelscene.h”中進(jìn)行聲明,然后在“chooselevelscene.cpp”中進(jìn)行重寫
代碼如下:
void ChooseLevelScene::paintEvent(QPaintEvent *)
{
//創(chuàng)建畫家,制定繪圖設(shè)備
QPainter painter(this);
//創(chuàng)建QPixmap對象
QPixmap pix;
//加載圖片
pix.load(":/res/OtherSceneBg.png");
//繪制背景圖
painter.drawPixmap(0,0,this->width(),this->height(),pix);
//加載標(biāo)題
pix.load(":/res/Title.png");
//繪制標(biāo)題
painter.drawPixmap( (this->width() - pix.width())*0.5,30,pix.width(),pix.height(),pix);
}
背景圖片設(shè)置完成后,我們來設(shè)置右下角的返回按鈕,在“chooselevelscene.cpp”中運(yùn)用自定義控件“mypushbutton”來創(chuàng)建返回按鈕。
代碼如下:
//返回按鈕
MyPushButton * closeBtn = new MyPushButton(":/res/BackButton.png",":/res/BackButtonSelected.png");
closeBtn->setParent(this);
//設(shè)置按鈕位置
closeBtn->move(this->width()-closeBtn->width(),this->height()-closeBtn->height());
因?yàn)榉祷匕粹o是有正常顯示圖片和點(diǎn)擊后顯示圖片的兩種模式,所以我們需要重寫MyPushButton中的 MousePressEvent和MouseReleaseEvent,從而實(shí)現(xiàn)不同的顯示效果。
先在“mypushbutton.h”中進(jìn)行聲明,然后在“mypushbutton.cpp”中實(shí)現(xiàn)
代碼如下:
//鼠標(biāo)事件
void MyPushButton::mousePressEvent(QMouseEvent *e)
{
if(pressedImgPath != "") //選中路徑不為空,顯示選中圖片
{
QPixmap pixmap;
bool ret = pixmap.load(pressedImgPath);
if(!ret)
{
qDebug() << pressedImgPath << "加載圖片失敗!";
}
this->setFixedSize( pixmap.width(), pixmap.height() );
this->setStyleSheet("QPushButton{border:0px;}");
this->setIcon(pixmap);
this->setIconSize(QSize(pixmap.width(),pixmap.height()));
}
//交給父類執(zhí)行按下事件
return QPushButton::mousePressEvent(e);
}
void MyPushButton::mouseReleaseEvent(QMouseEvent *e)
{
if(normalImgPath != "") //選中路徑不為空,顯示選中圖片
{
QPixmap pixmap;
bool ret = pixmap.load(normalImgPath);
if(!ret)
{
qDebug() << normalImgPath << "加載圖片失敗!";
}
this->setFixedSize( pixmap.width(), pixmap.height() );
this->setStyleSheet("QPushButton{border:0px;}");
this->setIcon(pixmap);
this->setIconSize(QSize(pixmap.width(),pixmap.height()));
}
//交給父類執(zhí)行 釋放事件
return QPushButton::mouseReleaseEvent(e);
}
運(yùn)行界面,結(jié)果如下:
3、返回按鈕的相關(guān)功能設(shè)置
我們點(diǎn)擊返回按鈕后,設(shè)置延時(shí)0.5秒后隱藏自身,并且發(fā)送自定義的信號“chooseSceneBack()”,告訴外界自身已經(jīng)選擇了返回按鈕。
我們先在“chooselevelscene.h”中聲明自定義信號(但不需要實(shí)現(xiàn))
然后在“chooselevelscene.cpp”中添加返回按鈕的功能:
代碼如下:
//返回按鈕功能實(shí)現(xiàn)
connect(closeBtn,&MyPushButton::clicked,[=](){
QTimer::singleShot(500, this,[=](){
this->hide();
//觸發(fā)自定義信號,關(guān)閉自身
emit this->chooseSceneBack();
}
);
});
在主場景MainScene中我們添加功能——點(diǎn)擊開始按鈕顯示選擇關(guān)卡的同時(shí),監(jiān)聽選擇關(guān)卡的返回按鈕消息,接受到返回按鈕消息的話則展示主場景界面。在“mainscene.cpp”中添加代碼
代碼如下:
//監(jiān)聽選擇場景的返回按鈕
connect(chooseScene,&ChooseLevelScene::chooseSceneBack,[=](){
this->show();
});
此時(shí)運(yùn)行項(xiàng)目,點(diǎn)擊開始按鈕切換到關(guān)卡選擇場景,點(diǎn)擊back按鈕返回主場景。
4、創(chuàng)建選擇關(guān)卡按鈕
現(xiàn)在我們需要實(shí)現(xiàn)下圖中20個(gè)關(guān)卡的按鈕,直接在“chooselevelscene.cpp”中添加代碼,代碼還是添加在構(gòu)造函數(shù)中。
代碼如下:
//創(chuàng)建關(guān)卡按鈕
for(int i = 0 ; i < 20;i++)
{
//運(yùn)用自定義控件來生成關(guān)卡按鈕
MyPushButton * menuBtn = new MyPushButton(":/res/LevelIcon.png");
menuBtn->setParent(this);
//設(shè)置每個(gè)關(guān)卡按鈕的位置 根據(jù)個(gè)人喜好還有前面設(shè)置的場景大小來設(shè)置
menuBtn->move(40 + (i%4)*70 , 135+ (i/4)*70);
//按鈕上顯示的文字
QLabel * label = new QLabel;
label->setParent(this);
label->setFixedSize(menuBtn->width(),menuBtn->height());
label->setText(QString::number(i+1));//顯示關(guān)卡數(shù)字
label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); //設(shè)置居中
label->move(40 + (i%4)*70 , 135+ (i/4)*70);//讓數(shù)字在按鈕上顯示
label->setAttribute(Qt::WA_TransparentForMouseEvents,true); //鼠標(biāo)事件穿透
}
5、實(shí)現(xiàn)點(diǎn)擊關(guān)卡按鈕跳轉(zhuǎn)到對應(yīng)的翻金幣場景功能
同前面的場景創(chuàng)建一樣,我們需要重新定義一個(gè)類——PlayScene,用來創(chuàng)建翻金幣場景,總共有20關(guān),但不需要?jiǎng)?chuàng)建20個(gè)類,只需要一個(gè)即可,不同關(guān)卡利用代碼實(shí)現(xiàn)即可。
選擇項(xiàng)目右鍵,選擇“Add New”-->選擇“C++”-->選擇“C++ Class”-->下一步-->設(shè)置類名“PlayScene”-->選擇父類“QMainWindow”-->下一步-->完成
創(chuàng)建成功夠項(xiàng)目結(jié)構(gòu)如下:
因?yàn)槲覀冃枰陉P(guān)卡選擇場景點(diǎn)擊對應(yīng)按鈕然后跳轉(zhuǎn)到對應(yīng)的游戲界面,因此我們可以通過在關(guān)卡選擇場景“chooselevelscene.h”中添加成員變量pScene用來指向不同關(guān)卡對應(yīng)的游戲界面。
然后在關(guān)卡選擇場景“chooselevelscene.cpp”中利用信號和槽,監(jiān)聽所選擇的關(guān)卡并使得上面定義的場景指針指向?qū)?yīng)的場景。此時(shí)new PlayScene(i+1)會(huì)報(bào)錯(cuò),因?yàn)槲覀儧]有重寫創(chuàng)建場景的構(gòu)造函數(shù)(下一部分實(shí)現(xiàn))。
六、翻金幣場景實(shí)現(xiàn)
1、設(shè)置游戲場景基本配置
創(chuàng)建對應(yīng)的關(guān)卡場景,需要接受關(guān)卡選擇界面?zhèn)鬟f過來的關(guān)卡號,因此我們在“playscene.h”中定義變量接收關(guān)卡號,同時(shí)聲明重載構(gòu)造函數(shù)
然后在“playscene.cpp”中重寫構(gòu)造函數(shù)來實(shí)現(xiàn)游戲場景的搭建,比如窗口大小、菜單欄等基礎(chǔ)功能實(shí)現(xiàn)
代碼如下:
PlayScene::PlayScene(int index)
{
//qDebug() << "當(dāng)前關(guān)卡為"<< index;
this->levalIndex = index;
//設(shè)置窗口固定大小
this->setFixedSize(320,588);
//設(shè)置圖標(biāo)
this->setWindowIcon(QPixmap(":/res/Coin0001.png"));
//設(shè)置標(biāo)題
this->setWindowTitle("翻金幣");
//創(chuàng)建菜單欄
QMenuBar * bar = this->menuBar();
this->setMenuBar(bar);
//創(chuàng)建開始菜單
QMenu * startMenu = bar->addMenu("開始");
//創(chuàng)建按鈕菜單項(xiàng)
QAction * quitAction = startMenu->addAction("退出");
//點(diǎn)擊退出 退出游戲
connect(quitAction,&QAction::triggered,[=](){this->close();});
}
2、設(shè)置游戲場景的背景圖片
跟前面一樣,需要重寫paintEvent函數(shù),先在“playscene.h”中進(jìn)行聲明,然后在“playscene.cpp”中進(jìn)行重寫
代碼如下:
void PlayScene::paintEvent(QPaintEvent *)
{
//加載背景
QPainter painter(this);
QPixmap pix;
pix.load(":/res/PlayLevelSceneBg.png");
painter.drawPixmap(0,0,this->width(),this->height(),pix);
//加載標(biāo)題
pix.load(":/res/Title.png");
pix = pix.scaled(pix.width()*0.5,pix.height()*0.5);
painter.drawPixmap( 10,30,pix.width(),pix.height(),pix);
}
此時(shí)運(yùn)行項(xiàng)目,點(diǎn)擊任意關(guān)卡進(jìn)入,游戲場景如下:
3、設(shè)置返回按鈕及其功能
與關(guān)卡選擇場景一樣,我們需要設(shè)置其按下的特效,還有就是自定義返回信號給關(guān)卡選擇場景用于捕捉,一旦捕捉到則返回關(guān)卡選擇場景。
先在“playscene.h”中聲明自定義信號,然后在“playscene.cpp”中創(chuàng)建返回按鈕并監(jiān)聽返回按鈕
代碼如下:
//返回按鈕
MyPushButton * closeBtn = new MyPushButton(":/res/BackButton.png",":/res/BackButtonSelected.png");
closeBtn->setParent(this);
closeBtn->move(this->width()-closeBtn->width(),this->height()-closeBtn->height());
//返回按鈕功能實(shí)現(xiàn)
connect(closeBtn,&MyPushButton::clicked,[=](){
QTimer::singleShot(500, this,[=](){
this->hide();
//觸發(fā)自定義信號,關(guān)閉自身,該信號寫到 signals下做聲明
emit this->chooseSceneBack();
}
);
});
然后在關(guān)卡選擇場景中設(shè)置監(jiān)聽palyscene的返回信號,在“chooselevelscene.cpp”中添加代碼
代碼如下:
//PlayScene的返回按鈕監(jiān)聽,刪除該scene并將指針置為空
connect(pScene,&PlayScene::chooseSceneBack,[=](){
this->setGeometry(pScene->geometry());
this->show();
delete pScene;
pScene = NULL;
});
4、在左下角顯示當(dāng)前關(guān)卡號
在“palyscene.cpp”中添加代碼來顯示關(guān)卡號
代碼如下:
//當(dāng)前關(guān)卡標(biāo)題
QLabel * label = new QLabel;
label->setParent(this);
QFont font;
font.setFamily("華文新魏");//設(shè)置字體
font.setPointSize(20);
label->setFont(font);
QString str = QString("Level: %1").arg(this->levalIndex);
label->setText(str);
label->setGeometry(QRect(15, this->height() - 50,170, 50)); //設(shè)置大小和位置
此時(shí)運(yùn)行程序,隨意選擇關(guān)卡可看到左下角關(guān)卡號,結(jié)果如下:
5、創(chuàng)建游戲界面中間的金幣背景
在“playscene.cpp”中添加代碼
代碼如下:
//創(chuàng)建金幣的背景圖片
for(int i = 0 ; i < 4;i++)
{
for(int j = 0 ; j < 4; j++)
{
//繪制背景圖片
QLabel* label = new QLabel;
label->setGeometry(0,0,50,50);//設(shè)置大小
label->setPixmap(QPixmap(":/res/BoardNode(1).png"));
label->setParent(this);
label->move(70 + i*50,200+j*50);//所在位置,對應(yīng)x,y
}
}
此時(shí)運(yùn)行項(xiàng)目,選擇任意關(guān)卡,游戲界面如下:
6、創(chuàng)建金幣類
我們在創(chuàng)建好金幣背景之后,則需要把金幣添加到對應(yīng)位置上。而金幣它需要能被點(diǎn)擊,然后點(diǎn)擊后還有翻轉(zhuǎn)特效,因此我們可以單獨(dú)將其封裝成一個(gè)金幣類方便使用與創(chuàng)建。
?同前面的場景創(chuàng)建一樣,我們需要重新定義一個(gè)類——MyCoin
選擇項(xiàng)目右鍵,選擇“Add New”-->選擇“C++”-->選擇“C++ Class”-->下一步-->設(shè)置類名“MyCoin”-->選擇父類“QWidget”-->下一步-->完成
創(chuàng)建好后項(xiàng)目結(jié)構(gòu)如下:
然后修改MyCoin類的父類為QPushButton
在資源圖片中,我們可以看到,金幣翻轉(zhuǎn)的效果是利用多張圖片切換而形成的,而以下八張圖片中,正常顯示的圖片為金幣Coin0001或者是銀幣 Coin0008這兩種圖。因此我們在創(chuàng)建一個(gè)金幣對象時(shí)候,應(yīng)該提供一個(gè)參數(shù),代表著傳入的是金幣資源路徑還是銀幣資源路徑,根據(jù)路徑我們創(chuàng)建不同樣式的圖案。所以我們要重寫金幣的構(gòu)造函數(shù)。
先在“mycoin.h”中聲明重載構(gòu)造函數(shù),然后在“mycoin.cpp”中實(shí)現(xiàn)重載構(gòu)造函數(shù)
代碼如下:
MyCoin::MyCoin(QString butImg)
{
QPixmap pixmap;
bool ret = pixmap.load(butImg);
if(!ret)
{
qDebug() << butImg << "加載圖片失敗!";
}
this->setFixedSize( pixmap.width(), pixmap.height() );
this->setStyleSheet("QPushButton{border:0px;}");
this->setIcon(pixmap);
this->setIconSize(QSize(pixmap.width(),pixmap.height()));
}
然后我們?nèi)ビ螒驁鼍爸刑砑咏饚?/p>
代碼如下:
//金幣對象
MyCoin * coin = new MyCoin(":/res/Coin0001.png");
coin->setParent(this);
coin->move(72 + i*50,204+j*50);
此時(shí)運(yùn)行項(xiàng)目,選擇任意關(guān)卡,游戲界面如下:
7、引入關(guān)卡數(shù)據(jù)
每個(gè)關(guān)卡的初始化界面都不一樣,因此需要我們引用一個(gè)現(xiàn)有的關(guān)卡文件,文件中記錄了各個(gè)關(guān)卡的金幣排列清空,也就是二維數(shù)組的數(shù)值。(文件在前言中有提供,就是dataconfig.h和dataconfig.cpp文件)
首先先將dataConfig.h 和 dataConfig.cpp文件放入到當(dāng)前項(xiàng)目下:
選擇項(xiàng)目右鍵,選擇“添加現(xiàn)有文件”-->分別選擇兩個(gè)文件添加
添加成功后項(xiàng)目結(jié)構(gòu)如下:
8、初始化各個(gè)關(guān)卡
首先,我們在playscene.h中聲明一個(gè)成員變量,用于記錄當(dāng)前關(guān)卡的二維數(shù)組。然后在playscene.cpp中初始化這個(gè)二維數(shù)組,記得添加頭文件“dataconfig.h”。添加代碼的位置可以自己選擇,可以放在前面,也可以跟我一樣放
代碼如下:
//初始化二維數(shù)組
dataConfig config;
for(int i = 0 ; i < 4;i++){
for(int j = 0 ; j < 4; j++)
{
gameArray[i][j] = config.mData[this->levalIndex][i][j];
}
}
初始化成功后,我們需要在MyCoin類中,定義屬性 posX,posY,以及flag,這三個(gè)屬性分別代表了,該金幣在二維數(shù)組中 x的坐標(biāo),y的坐標(biāo),以及當(dāng)前的正反標(biāo)志。(正反標(biāo)志是用于判斷該圖片是金幣還是銀幣)
然后對金幣進(jìn)一步進(jìn)行初始化,前面的添加金幣僅僅只是添加金幣,看金幣是否創(chuàng)建成功,現(xiàn)在就是對關(guān)卡進(jìn)行初始化,有金幣也有銀幣
代碼如下:
//金幣對象
QString img;
if(gameArray[i][j] == 1)
{
img = ":/res/Coin0001.png";
}
else
{
img = ":/res/Coin0008.png";
}
MyCoin * coin = new MyCoin(img);
coin->setParent(this);
coin->move(72 + i*50,204+j*50);
coin->posX = i; //記錄x坐標(biāo)
coin->posY = j; //記錄y坐標(biāo)
coin->flag =gameArray[i][j]; //記錄正反標(biāo)志
此時(shí)運(yùn)行項(xiàng)目,任意點(diǎn)擊關(guān)卡,可以看到游戲界面已經(jīng)初始化成功,結(jié)果如下:
9、實(shí)現(xiàn)翻金幣特效
關(guān)卡的初始化完成后,下面就實(shí)現(xiàn)點(diǎn)擊金幣進(jìn)行翻轉(zhuǎn)的效果,我們在MyCoin類中實(shí)現(xiàn)該方法。先在“mycoin.h”中進(jìn)行聲明,然后在“mycoin.cpp”中實(shí)現(xiàn)翻轉(zhuǎn)方法,同時(shí)在構(gòu)造函數(shù)中創(chuàng)建定時(shí)器
代碼如下:
//初始化定時(shí)器
timer1 = new QTimer(this);
timer2 = new QTimer(this);
void MyCoin::changeFlag()
{
if(this->flag) //如果是正面,執(zhí)行下列代碼
{
timer1->start(30);
this->flag = false;
}
else //反面執(zhí)行下列代碼
{
timer2->start(30);
this->flag = true;
}
}
設(shè)置好了之后我們就要在構(gòu)造函數(shù)中實(shí)現(xiàn)金幣和銀幣的翻轉(zhuǎn)效果,代碼添加在剛才設(shè)置的定時(shí)器后面就可以了
代碼如下:
//監(jiān)聽正面翻轉(zhuǎn)的信號槽
connect(timer1,&QTimer::timeout,[=](){
QPixmap pixmap;
QString str = QString(":/res/Coin000%1.png").arg(this->min++);
pixmap.load(str);
this->setFixedSize(pixmap.width(),pixmap.height() );
this->setStyleSheet("QPushButton{border:0px;}");
this->setIcon(pixmap);
this->setIconSize(QSize(pixmap.width(),pixmap.height()));
if(this->min > this->max) //如果大于最大值,重置最小值,并停止定時(shí)器
{
this->min = 1;
timer1->stop();
}
});
connect(timer2,&QTimer::timeout,[=](){
QPixmap pixmap;
QString str = QString(":/res/Coin000%1.png").arg((this->max)-- );
pixmap.load(str);
this->setFixedSize(pixmap.width(),pixmap.height() );
this->setStyleSheet("QPushButton{border:0px;}");
this->setIcon(pixmap);
this->setIconSize(QSize(pixmap.width(),pixmap.height()));
if(this->max < this->min) //如果小于最小值,重置最大值,并停止定時(shí)器
{
this->max = 8;
timer2->stop();
}
});
接著我們監(jiān)聽每個(gè)按鈕的點(diǎn)擊效果,并翻轉(zhuǎn)金幣,查看翻轉(zhuǎn)特效是否成功(此時(shí)只是測試,因此只有點(diǎn)擊的金幣會(huì)進(jìn)行翻轉(zhuǎn))
代碼如下:
connect(coin,&MyCoin::clicked,[=](){
//qDebug() << "點(diǎn)擊的位置: x = " << coin->posX << " y = " << coin->posY ;
coin->changeFlag();
gameArray[i][j] = gameArray[i][j] == 0 ? 1 : 0; //數(shù)組內(nèi)部記錄的標(biāo)志同步修改
});
此時(shí)運(yùn)行項(xiàng)目,任意選擇關(guān)卡,點(diǎn)擊任意金幣,查看特效:
此時(shí)翻轉(zhuǎn)金幣特效已經(jīng)實(shí)現(xiàn)了,但是如果快速點(diǎn)擊,會(huì)在金幣還沒有執(zhí)行一個(gè)完整動(dòng)作之后 ,又繼續(xù)開始新的動(dòng)畫,我們應(yīng)該在金幣做動(dòng)畫期間,禁止再次點(diǎn)擊,并在完成動(dòng)畫后,開啟點(diǎn)擊。
我們可以在MyCoin類中加入一個(gè)標(biāo)志 isAnimation 代表是否正在做翻轉(zhuǎn)動(dòng)畫。并且在changFlag()函數(shù)中將標(biāo)志設(shè)為true,在做完動(dòng)畫后修改為false
同時(shí)我們重寫按鈕的按下事件,判斷如果正在執(zhí)行動(dòng)畫,那么直接return掉,不要執(zhí)行后續(xù)代碼。先在“mycoin.h”聲明,然后在“mycoin.cpp”實(shí)現(xiàn)
代碼如下:
void MyCoin::mousePressEvent(QMouseEvent *e)
{
if(this->isAnimation )
{
return;
}
else
{
return QPushButton::mousePressEvent(e);
}
}
10、實(shí)現(xiàn)周圍金幣跟著翻轉(zhuǎn)功能
在用戶點(diǎn)擊金幣翻轉(zhuǎn)的同時(shí),我們讓其上下左右的4個(gè)金幣也進(jìn)行延時(shí)翻轉(zhuǎn),代碼寫到監(jiān)聽點(diǎn)擊金幣下。此時(shí)我們還需要記錄每個(gè)按鈕的內(nèi)容,所以我們將所有金幣按鈕也放到一個(gè)二維數(shù)組中,在playscene.h中聲明
然后設(shè)置延時(shí)翻動(dòng)其他周圍金幣,代碼添加在“playscene.cpp”中
代碼如下:
//翻轉(zhuǎn)周圍硬幣
QTimer::singleShot(300, this,[=](){
//周圍右側(cè)的硬幣翻轉(zhuǎn)條件
if(coin->posX+1 <=3)
{
coinBtn[coin->posX+1][coin->posY]->changeFlag();
gameArray[coin->posX+1][coin->posY] = gameArray[coin->posX+1][coin->posY]== 0 ? 1 : 0;
}
//周圍左側(cè)的硬幣翻轉(zhuǎn)條件
if(coin->posX-1>=0)
{
coinBtn[coin->posX-1][coin->posY]->changeFlag();
gameArray[coin->posX-1][coin->posY] = gameArray[coin->posX-1][coin->posY]== 0 ? 1 : 0;
}
//周圍上側(cè)的硬幣翻轉(zhuǎn)條件
if(coin->posY+1<=3)
{
coinBtn[coin->posX][coin->posY+1]->changeFlag();
gameArray[coin->posX][coin->posY+1] = gameArray[coin->posX+1][coin->posY]== 0 ? 1 : 0;
}
//周圍下側(cè)硬幣翻轉(zhuǎn)條件
if(coin->posY-1>=0)
{
coinBtn[coin->posX][coin->posY-1]->changeFlag();
gameArray[coin->posX][coin->posY-1] = gameArray[coin->posX+1][coin->posY]== 0 ? 1 : 0;
}
});
11、判斷是否勝利
我們在在playscene.h中加入 isWin標(biāo)志,代表是否勝利。
默認(rèn)設(shè)置為true,只要有一個(gè)銀幣,就將該值改為false,視為未成功。代碼寫到延時(shí)翻金幣后 進(jìn)行判斷。
代碼如下:
//判斷是否勝利
this->isWin = true;
for(int i = 0 ; i < 4;i++)
{
for(int j = 0 ; j < 4; j++)
{
//qDebug() << coinBtn[i][j]->flag ;
if( coinBtn[i][j]->flag == false)
{
this->isWin = false;
break;
}
}
}
然后進(jìn)行總的判斷
12、顯示勝利圖片
我們將勝利的圖片提前創(chuàng)建好,如果勝利觸發(fā)了,將圖片彈下來即可。創(chuàng)建勝利圖片的代碼在前面選個(gè)位置添加就可以了
代碼如下:
//創(chuàng)建勝利圖片
QLabel* winLabel = new QLabel;
QPixmap tmpPix;
tmpPix.load(":/res/LevelCompletedDialogBg.png");
winLabel->setGeometry(0,0,tmpPix.width(),tmpPix.height());
winLabel->setPixmap(tmpPix);
winLabel->setParent(this);
winLabel->move( (this->width() - tmpPix.width())*0.5 , -tmpPix.height());
然后就是在判斷勝利之后彈出此圖片
代碼如下:
//如果isWin依然是true,代表勝利了!
if(this->isWin)
{
qDebug() << "勝利";
QPropertyAnimation * animation1 = new QPropertyAnimation(winLabel,"geometry");
animation1->setDuration(1000);
animation1->setStartValue(QRect(winLabel->x(),winLabel->y(),winLabel->width(),winLabel->height()));
animation1->setEndValue(QRect(winLabel->x(),winLabel->y()+114,winLabel->width(),winLabel->height()));
animation1->setEasingCurve(QEasingCurve::OutBounce);
animation1->start();
}
13、勝利后禁用按鈕
當(dāng)勝利后,應(yīng)該禁用所有按鈕的點(diǎn)擊狀態(tài),可以在每個(gè)按鈕中加入標(biāo)志位 isWin,如果isWin為true,MousePressEvent直接return掉即可。
修改MousePressEvent中的代碼
在判斷勝利后禁用所有金幣
代碼如下:
//將所有按鈕的勝利標(biāo)志改為true,即禁用
for(int i = 0 ; i < 4;i++)
{
for(int j = 0 ; j < 4; j++)
{
coinBtn[i][j]->isWin = true;
}
}
七、添加音效
在“mainscene.cpp”中添加開始音效
QSound *startSound = new QSound(":/res/TapButtonSound.wav",this);
在后面添加上“multimedia”,然后重新構(gòu)建一下即可
播放音效
在“chooselevelscene.cpp”中添加選擇關(guān)卡音效
//選擇關(guān)卡按鈕音效
QSound *chooseSound = new QSound(":/res/TapButtonSound.wav",this);
在“chooselevelscene.cpp”還有“playscene.cpp”中分別添加返回按鈕音效
//返回按鈕音效
QSound *backSound = new QSound(":/res/BackButtonSound.wav",this);
在playscene.cpp中添加,翻金幣的音效以及勝利的音效
//翻金幣音效
QSound *flipSound = new QSound(":/res/ConFlipSound.wav",this);
//勝利按鈕音效
QSound *winSound = new QSound(":/res/LevelWinSound.wav",this);
八、優(yōu)化項(xiàng)目
1、切換三個(gè)場景的進(jìn)入與返回都在同一個(gè)位置下
當(dāng)我們移動(dòng)場景后,如果進(jìn)入下一個(gè)場景,發(fā)現(xiàn)場景還在中心位置,如果想設(shè)置場景的位置,需要添加如下下圖中的代碼:
在MainScene.cpp中添加:
在ChooseScene.cpp中添加:
2、在游戲勝利瞬間點(diǎn)了其他金幣使得未勝利(速度過快)
解決這一bug,只需要我們設(shè)置在點(diǎn)擊翻轉(zhuǎn)的那一瞬間把所有按鈕都禁用,在周圍的金幣都翻轉(zhuǎn)之后再恢復(fù)
九、打包項(xiàng)目
點(diǎn)擊左邊的“電腦”-->選中項(xiàng)目-->選擇“release”-->然后等待構(gòu)建成功,可以再運(yùn)行一遍
打開項(xiàng)目所在路徑-->找到對應(yīng)結(jié)尾為Release的文件夾-->點(diǎn)擊“release”文件夾-->復(fù)制一份exe文件
在桌面任意創(chuàng)建一個(gè)文件夾-->將剛才復(fù)制的CoinFlip.exe復(fù)制粘貼到里面
在開始那里找到命令窗口打開-->輸入cd 文件目錄 轉(zhuǎn)到桌面文件夾目錄下-->輸入
“windeployqt CoinFlip.exe”按下回車鍵等待完成
這樣就是完成了,此時(shí)桌面的文件夾也會(huì)多很多文件
這樣可執(zhí)行程序有很多依賴選項(xiàng),如果需要把程序給其他人使用就需要發(fā)整個(gè)文件夾,很麻煩
因此我們把整個(gè)文件夾打包為一個(gè)單一的可執(zhí)行.exe文件。
安裝Enigma virtual box工具(安裝過程一直next就行)
Enigma virtual box官方鏈接:https://enigmaprotector.com/en/downloads.html
點(diǎn)擊Add-->Add Folder Recursive-->選中文件夾-->OK
點(diǎn)擊Files Options-->Compress Files-->OK-->點(diǎn)擊右下角Process生成即可
然后打開文件夾,所生成的CoinFlip_boxed.exe就是打包好的程序了,想要發(fā)給別人的就只用發(fā)這一個(gè)文件就可以了文章來源:http://www.zghlxwxcb.cn/news/detail-705520.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-705520.html
到了這里,關(guān)于QT實(shí)戰(zhàn)之翻金幣游戲【未完待續(xù)】的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!