定制化網(wǎng)站開(kāi)發(fā)一般多少錢網(wǎng)頁(yè)制作三大軟件
文章目錄
- 1 項(xiàng)目簡(jiǎn)介
- 2 項(xiàng)目基本配置
- 2.1 創(chuàng)建項(xiàng)目
- 2.2 添加資源
- 3 主場(chǎng)景
- 3.1 設(shè)置游戲主場(chǎng)景配置
- 3.2 設(shè)置背景圖片
- 3.3 創(chuàng)建開(kāi)始按鈕
- 3.4 開(kāi)始按鈕跳躍特效實(shí)現(xiàn)
- 3.5 創(chuàng)建選擇關(guān)卡場(chǎng)景
- 3.6 點(diǎn)擊開(kāi)始按鈕進(jìn)入選擇關(guān)卡場(chǎng)景
- 4 選擇關(guān)卡場(chǎng)景
- 4.1場(chǎng)景基本設(shè)置
- 4.2 背景設(shè)置
- 4.3 創(chuàng)建返回按鈕
- 4.4 選擇關(guān)卡的返回按鈕特效制作
- 4.5 返回按鈕
- 4.5.1 開(kāi)始場(chǎng)景與選擇關(guān)卡場(chǎng)景的切換
- 4.6 創(chuàng)建選擇關(guān)卡按鈕
- 4.7 創(chuàng)建翻金幣場(chǎng)景
- 5 翻金幣場(chǎng)景
- 5.1 場(chǎng)景基本設(shè)置
- 5.2 背景設(shè)置
- 5.3 返回按鈕
- 5.4 顯示當(dāng)前關(guān)卡
- 5.5 創(chuàng)建金幣背景圖片
- 5.6 創(chuàng)建金幣類
- 5.6.1 創(chuàng)建金幣類 MyCoin
- 5.6.2 構(gòu)造函數(shù)
- 5.6.3測(cè)試
- 5.7引入關(guān)卡數(shù)據(jù)
- 5.7.1 添加現(xiàn)有文件dataConfig
- 5.7.2 添加現(xiàn)有文件
- 5.7.3 完成添加
- 5.7.4 數(shù)據(jù)分析
- 5.7.5 測(cè)試關(guān)卡數(shù)據(jù)
- 5.8 初始化各個(gè)關(guān)卡
- 5.9 翻金幣特效
- 5.9.1 MyCoin類擴(kuò)展屬性和行為
- 5.9.2 創(chuàng)建特效
- 5.9.3 禁用按鈕
- 5.9.4 翻周圍金幣
- 5.10 判斷是否勝利
- 5.11 勝利圖片顯示
- 5.12 勝利后禁用按鈕
- 6 音效添加
- 6.1 開(kāi)始音效
- 6.2 選擇關(guān)卡音效
- 6.3 返回按鈕音效
- 6.4 翻金幣與勝利音效
- 7 優(yōu)化項(xiàng)目
- 8 項(xiàng)目打包
1 項(xiàng)目簡(jiǎn)介
翻金幣項(xiàng)目是一款經(jīng)典的益智類游戲,我們需要將金幣都翻成同色,才視為勝利。首先,開(kāi)始界面如下 :
點(diǎn)擊start按鈕,進(jìn)入下層界面,選擇關(guān)卡:
在這里我們?cè)O(shè)立了20個(gè)關(guān)卡供玩家選擇,假設(shè)我們點(diǎn)擊了第1關(guān),界面如下:
如果想要贏取勝利,我們需要點(diǎn)擊上圖中紅色方框選取的區(qū)域,翻動(dòng)其上下左右的金幣,然后當(dāng)所有金幣都變?yōu)榻鹕?#xff0c;視為勝利,勝利界面如下:
2 項(xiàng)目基本配置
2.1 創(chuàng)建項(xiàng)目
打開(kāi)Qt-Creator,創(chuàng)建項(xiàng)目:注意名稱不要包含空格和回車,路徑不要有中文
類信息中,選擇基類為QMainWindow,類名稱為MainScene,代表著主場(chǎng)景
點(diǎn)擊完成,創(chuàng)建出項(xiàng)目:
創(chuàng)建的項(xiàng)目結(jié)構(gòu)如下:
2.2 添加資源
將資源添加到當(dāng)前項(xiàng)目下
然后創(chuàng)建.qrc文件
進(jìn)入編輯模式,添加前綴 “ / ”,添加文件
將所有資源文件進(jìn)行添加
至此將所有需要的資源添加到了本項(xiàng)目中
3 主場(chǎng)景
3.1 設(shè)置游戲主場(chǎng)景配置
點(diǎn)擊mainscene.ui文件,設(shè)計(jì)其菜單欄如下
設(shè)計(jì)“退出”菜單項(xiàng),oblectName為actionQult,text為退出;
移除自帶的工具欄與狀態(tài)欄
回到MainScene.cpp文件,進(jìn)入構(gòu)造函數(shù)中,進(jìn)行場(chǎng)景的基本配置,代碼如下:
// 設(shè)置固定大小this->setFixedSize(390, 570);// 設(shè)置圖標(biāo)this->setWindowIcon(QIcon(":/res/Coin0001.png"));// 設(shè)置窗口標(biāo)題this->setWindowTitle("翻金幣主場(chǎng)景");
運(yùn)行效果如圖:
3.2 設(shè)置背景圖片
重寫MainScene 的 PaintEvent 事件,并添加一下代碼,繪制背景圖片
// MainScene.h// 重寫paintEvent事件 畫背景圖void paintEvent(QPaintEvent *);
// MainScene.cpp
void MainScene::paintEvent(QPaintEvent *) {// 創(chuàng)建畫家 指定繪圖設(shè)備QPainter painter(this);// 創(chuàng)建QPixmap對(duì)象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);
}
3.3 創(chuàng)建開(kāi)始按鈕
開(kāi)始按鈕點(diǎn)擊后有彈跳效果,這個(gè)效果是我們利用自定義控件實(shí)現(xiàn)的(QPushButton不會(huì)自帶這類特效),我們可以自己封裝出一個(gè)按鈕控件,來(lái)實(shí)現(xiàn)這些效果。
創(chuàng)建MyPushButton,繼承與 QPushButton
點(diǎn)擊完成。
修改MyPushButton的父類
提供MyPushButton 的構(gòu)造的重載版本,可以讓 MyPushButton 提供正常顯示的圖片以及按下后顯示的圖片
代碼如下
// mypushbutton.h// 構(gòu)造函數(shù) 參數(shù)1 正常顯示的圖片路徑 參數(shù)2 按下后顯示路徑MyPushButton(QString normalImg, QString pressImg = "");// 成員屬性 保存用戶傳入的默認(rèn)顯示路徑 以及按下后顯示的圖片路徑QString normalImgPath;QString pressImgPath;
實(shí)現(xiàn)的重載版本MyPushButton構(gòu)造函數(shù)代碼如下
MyPushButton::MyPushButton(QString normalImg, QString pressImg) {this->normalImgPath = normalImg;this->pressImgPath = pressImg;QPixmap pix;bool ret = pix.load(normalImg);if (!ret) {qDebug() << "圖片加載失敗";return;}// 設(shè)置圖片固定大小this->setFixedSize(pix.width(), pix.height());// 設(shè)置不規(guī)則圖片樣式this->setStyleSheet("QPushButton{ border: 0px;}");// 設(shè)置圖標(biāo)this->setIcon(pix);// 設(shè)置圖標(biāo)大小this->setIconSize(QSize(pix.width(), pix.height()));}
回到MainScene的構(gòu)造函數(shù)中,創(chuàng)建開(kāi)始按鈕
MyPushButton *startBtn = new MyPushButton(":/res/MenuSceneStartButton.png");startBtn->setParent(this);startBtn->move(this->width() * 0.5 - startBtn->width() * 0.5, this->height() * 0.7);
不規(guī)則的開(kāi)始按鈕添加完成。
3.4 開(kāi)始按鈕跳躍特效實(shí)現(xiàn)
連接信號(hào)槽,監(jiān)聽(tīng)開(kāi)始按鈕點(diǎn)擊
// mainscene.cpp
connect(startBtn, &QPushButton::clicked, this, [=](){// 做彈起特效startBtn->zoom1();startBtn->zoom2();});
zoom1 與 zoom2 為 MyPushButton 中擴(kuò)展的特效代碼,具體如下
// mypushbutton.cpp
void MyPushButton::zoom1() {// 創(chuàng)建動(dòng)態(tài)對(duì)象QPropertyAnimation *animation = new QPropertyAnimation(this, "geometry");// 設(shè)置動(dòng)畫時(shí)間間隔animation->setDuration(200);// 起始位置animation->setStartValue(QRect(this->x(), this->y(), this->width(), this->height()));// 結(jié)束位置animation->setEndValue(QRect(this->x(), this->y() + 10, this->width(), this->height()));// 設(shè)置彈跳曲線animation->setEasingCurve(QEasingCurve::OutBounce);// 執(zhí)行動(dòng)畫animation->start();
}
void MyPushButton::zoom2() {// 創(chuàng)建動(dòng)態(tài)對(duì)象QPropertyAnimation *animation = new QPropertyAnimation(this, "geometry");// 設(shè)置動(dòng)畫時(shí)間間隔animation->setDuration(200);// 起始位置animation->setStartValue(QRect(this->x(), this->y() + 10, this->width(), this->height()));// 結(jié)束位置animation->setEndValue(QRect(this->x(), this->y(), this->width(), this->height()));// 設(shè)置彈跳曲線animation->setEasingCurve(QEasingCurve::OutBounce);// 執(zhí)行動(dòng)畫animation->start();
}
3.5 創(chuàng)建選擇關(guān)卡場(chǎng)景
點(diǎn)擊開(kāi)始按鈕后,進(jìn)入選擇關(guān)卡場(chǎng)景。
首先我們先創(chuàng)建選擇關(guān)卡場(chǎng)景,添加新的C++文件
類名為ChooseLevelScene選擇基類為QMainWindow,點(diǎn)擊下一步,然后點(diǎn)擊完成
3.6 點(diǎn)擊開(kāi)始按鈕進(jìn)入選擇關(guān)卡場(chǎng)景
目前點(diǎn)擊主場(chǎng)景的開(kāi)始按鈕,只有彈跳特效,但是我們還需要有功能上的實(shí)現(xiàn),特效結(jié)束后,我們應(yīng)該進(jìn)入選擇關(guān)卡場(chǎng)景
在MainScene.h中保存ChooseScene選擇關(guān)卡場(chǎng)景對(duì)象
// 在mainscene.h中
// ChooseLevelScene *chooseScene = nullptr;// 在mainscene.cpp中
// 實(shí)例化選擇關(guān)卡場(chǎng)景chooseScene = new ChooseLevelScene;
我們?cè)趜oom1和zoom2特效后,延時(shí)0.5秒,進(jìn)入選擇關(guān)卡場(chǎng)景,代碼如下
// mainscene.cpp
connect(startBtn, &QPushButton::clicked, this, [=](){// 做彈起特效startBtn->zoom1(); // 向下跳躍startBtn->zoom2(); // 向上跳躍// 延時(shí)0.5秒 進(jìn)入到選擇關(guān)卡場(chǎng)景中QTimer::singleShot(500, this, [=](){// 自身隱藏this->hide();// 顯示選擇關(guān)卡場(chǎng)景chooseScene->show();});});
測(cè)試點(diǎn)擊開(kāi)始,執(zhí)行特效后延時(shí) 0.5 秒進(jìn)入選擇關(guān)卡場(chǎng)景
4 選擇關(guān)卡場(chǎng)景
4.1場(chǎng)景基本設(shè)置
選擇關(guān)卡構(gòu)造函數(shù)如下
// chooselevelscene.cpp
// 配置選擇關(guān)卡場(chǎng)景this->setFixedSize(390, 570);// 設(shè)置圖標(biāo)this->setWindowIcon(QPixmap(":/res/Coin0001.png"));// 設(shè)置標(biāo)題this->setWindowTitle("選擇關(guān)卡場(chǎng)景");// 創(chuàng)建菜單欄QMenuBar *bar = new QMenuBar();this->setMenuBar(bar);// 創(chuàng)建開(kāi)始菜單QMenu *startMenu = bar->addMenu("開(kāi)始");// 創(chuàng)建退出 菜單項(xiàng)QAction *quitAction = startMenu->addAction("退出");// 點(diǎn)擊退出 實(shí)現(xiàn)退出游戲connect(quitAction, &QAction::triggered, this, [=](){this->close();});
運(yùn)行效果如圖:
4.2 背景設(shè)置
// chooselevelscene.cpp
void ChooseLevelScene::paintEvent(QPaintEvent *) {// 加載背景QPainter painter(this);QPixmap pix;pix.load(":/res/OtherSceneBg.png");painter.drawPixmap(0, 0, this->width(), this->height(), pix);// 加載標(biāo)題pix.load(":/res/Title.png");painter.drawPixmap((this->width() - pix.width()) * 0.5, 30, pix.width(), pix.height(), pix);
}
4.3 創(chuàng)建返回按鈕
// chooselevelscene.cpp
// 返回按鈕MyPushButton *backBtn = new MyPushButton(":/res/BackButton.png", ":/res/BackButtonSelected.png");backBtn->setParent(this);backBtn->move(this->width() - backBtn->width(), this->height() - backBtn->height());
4.4 選擇關(guān)卡的返回按鈕特效制作
返回按鈕是有正常顯示圖片和點(diǎn)擊后顯示圖片的兩種模式,所以我們需要重寫MyPushButton
中的MousePressEvent
和 MouseReleaseEvent
// mypushbutton.h
// 重寫按鈕 按下 和 釋放事件void mousePressEvent(QMouseEvent *e);void mouseReleaseEvent(QMouseEvent *e);// mypushbutton.cpp
void MyPushButton::mousePressEvent(QMouseEvent *e) {// 傳入的按鈕圖片不為空 說(shuō)明需要有按下?tīng)顟B(tài), 切換圖片if (this->pressImgPath != "") {QPixmap pix;bool ret = pix.load(this->pressImgPath);if (!ret) {qDebug() << "圖片加載失敗";return;}// 設(shè)置圖片固定大小this->setFixedSize(pix.width(), pix.height());// 設(shè)置不規(guī)則圖片樣式this->setStyleSheet("QPushButton{ border: 0px;}");// 設(shè)置圖標(biāo)this->setIcon(pix);// 設(shè)置圖標(biāo)大小this->setIconSize(QSize(pix.width(), pix.height()));}// 讓父類執(zhí)行其他內(nèi)容return QPushButton::mousePressEvent(e);
}
void MyPushButton::mouseReleaseEvent(QMouseEvent *e) {// 傳入的按鈕圖片不為空 說(shuō)明需要有按下?tīng)顟B(tài), 切換成初始圖片if (this->pressImgPath != "") {QPixmap pix;bool ret = pix.load(this->normalImgPath);if (!ret) {qDebug() << "圖片加載失敗";return;}// 設(shè)置圖片固定大小this->setFixedSize(pix.width(), pix.height());// 設(shè)置不規(guī)則圖片樣式this->setStyleSheet("QPushButton{ border: 0px;}");// 設(shè)置圖標(biāo)this->setIcon(pix);// 設(shè)置圖標(biāo)大小this->setIconSize(QSize(pix.width(), pix.height()));}// 讓父類執(zhí)行其他內(nèi)容return QPushButton::mouseReleaseEvent(e);
}
4.5 返回按鈕
4.5.1 開(kāi)始場(chǎng)景與選擇關(guān)卡場(chǎng)景的切換
- 點(diǎn)擊選擇關(guān)卡場(chǎng)景的返回按鈕,發(fā)送一個(gè)自定義信號(hào)
- 在主場(chǎng)景中監(jiān)聽(tīng)這個(gè)信號(hào),并且當(dāng)觸發(fā)信號(hào)后,重新顯示主場(chǎng)景,隱藏掉選擇關(guān)卡的場(chǎng)景
在這里我們點(diǎn)擊返回后,延時(shí)0.5后隱藏自身,并且發(fā)送自定義信號(hào),告訴外界自身已經(jīng)選擇了返回按鈕
// chooselevelscene.h// 寫一個(gè)自定義信號(hào), 告訴主場(chǎng)景 點(diǎn)擊了返回void chooseSceneBack();// chooselevelscene.cpp// 點(diǎn)擊返回connect(backBtn, &QPushButton::clicked, this, [=](){// 告訴主場(chǎng)景 我返回了, 主場(chǎng)景監(jiān)聽(tīng)ChooseLevelScene的返回按鈕// 延時(shí)返回QTimer::singleShot(500, this, [=](){// 發(fā)送自定義信號(hào)emit this->chooseSceneBack();});});
在主場(chǎng)景MainScene中點(diǎn)擊開(kāi)始按鈕顯示選擇關(guān)卡的同時(shí),監(jiān)聽(tīng)選擇關(guān)卡的返回按鈕消息
// mainscene.cpp// 監(jiān)聽(tīng)選擇關(guān)卡場(chǎng)景的返回按鈕的信號(hào)connect(chooseScene, &ChooseLevelScene::chooseSceneBack, this, [=](){chooseScene->hide(); // 將選擇關(guān)卡場(chǎng)景 隱蔽掉this->show(); // 重新顯示主場(chǎng)景});
測(cè)試主場(chǎng)景與選擇關(guān)卡場(chǎng)景的切換功能
4.6 創(chuàng)建選擇關(guān)卡按鈕
選擇關(guān)卡中的按鈕創(chuàng)建
- 利用一個(gè)for循環(huán)將所有按鈕布置到場(chǎng)景中
- 在按鈕上面設(shè)置一個(gè)aLabel顯示關(guān)卡數(shù)
- QLabel設(shè)置大小、顯示文字、對(duì)齊方式、鼠標(biāo)穿透8.3給每個(gè)按鈕監(jiān)聽(tīng)點(diǎn)擊事件
// chooselevelscene.cpp
// 創(chuàng)建選擇關(guān)卡的按鈕for (int i = 0; i < 20; i++) {MyPushButton *menuBtn = new MyPushButton(":/res/LevelIcon.png");menuBtn->setParent(this);menuBtn->move(60 + i % 4 * 70, 140 + i / 4 * 70);// 監(jiān)聽(tīng)每個(gè)按鈕的點(diǎn)擊事件connect(menuBtn, &QPushButton::clicked, this, [=](){QString str = QString("第 %1 關(guān)").arg(i + 1);qDebug() << str;});QLabel *label = new QLabel;label->setParent(this);label->setFixedSize(menuBtn->width(), menuBtn->height());label->setText(QString::number(1 + i));label->move(60 + i % 4 * 70, 140 + i / 4 * 70);// 設(shè)置label上的文字對(duì)齊方式 水平居中 垂直居中l(wèi)abel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);// 設(shè)置讓鼠標(biāo)進(jìn)行穿透 51屬性label->setAttribute(Qt::WA_TransparentForMouseEvents);}
4.7 創(chuàng)建翻金幣場(chǎng)景
點(diǎn)擊關(guān)卡按鈕后,會(huì)進(jìn)入游戲的核心場(chǎng)景,也就是翻金幣的場(chǎng)景,首先先創(chuàng)建出該場(chǎng)景的h和.cpp文件創(chuàng)建PlayScene
點(diǎn)擊選擇關(guān)卡按鈕后會(huì)跳入到該場(chǎng)景
建立點(diǎn)擊按鈕,跳轉(zhuǎn)場(chǎng)景的信號(hào)槽連接
在ChooseLevelScene.h中聲明
PlayScene *pScene = NULL;
// chooselevelscene.cpp// 監(jiān)聽(tīng)每個(gè)按鈕的點(diǎn)擊事件connect(menuBtn, &QPushButton::clicked, this, [=](){QString str = QString("選了第 %1 關(guān)").arg(i + 1);qDebug() << str;// 進(jìn)入到游戲場(chǎng)景this->hide(); // 將選關(guān)場(chǎng)景隱藏掉play = new PlayScene(i + 1); // 創(chuàng)建游戲場(chǎng)景play->show(); // 顯示游戲場(chǎng)景});
這里pScene=newPlayScene(i+1):將用戶所選的關(guān)卡號(hào)發(fā)送給pScene,也就是翻金幣場(chǎng)景,當(dāng)然PlayScene要提供重載的有參構(gòu)造版本,來(lái)接受這個(gè)參數(shù)
5 翻金幣場(chǎng)景
5.1 場(chǎng)景基本設(shè)置
PlayScene.h中聲明成員變量,用于記錄當(dāng)前用戶選擇的關(guān)卡
// PlayScene.h
int levelIndex; // 內(nèi)部成員屬性 記錄所選的關(guān)卡
PlayScene.cpp中初始化該場(chǎng)景配置
// PlayScene.cpp
PlayScene::PlayScene(int levelNum) {QString str = QString("進(jìn)入第 %1 關(guān)").arg(levelNum);qDebug() << str;this->levelIndex = levelNum;// 初始化游戲場(chǎng)景// 設(shè)置固定大小this->setFixedSize(390, 570);// 設(shè)置圖標(biāo)this->setWindowIcon(QPixmap(":/res/Coin0001.png"));// 設(shè)置標(biāo)題this->setWindowTitle("翻金幣場(chǎng)景");// 創(chuàng)建菜單欄QMenuBar *bar = new QMenuBar();this->setMenuBar(bar);// 創(chuàng)建開(kāi)始菜單QMenu *startMenu = bar->addMenu("開(kāi)始");// 創(chuàng)建退出 菜單項(xiàng)QAction *quitAction = startMenu->addAction("退出");// 點(diǎn)擊退出 實(shí)現(xiàn)退出游戲connect(quitAction, &QAction::triggered, this, [=](){this->close();});
}
5.2 背景設(shè)置
// PlayScene.cpp
void PlayScene::paintEvent(QPaintEvent *) {// 創(chuàng)建背景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);
}
5.3 返回按鈕
// PlayScene.h
// 寫一個(gè)自定義信號(hào), 告訴選關(guān)場(chǎng)景 點(diǎn)擊了返回void chooseSceneBack();// PlayScene.cpp
// 點(diǎn)擊返回connect(backBtn, &QPushButton::clicked, this, [=](){// 告訴選關(guān)場(chǎng)景 我返回了, 選關(guān)場(chǎng)景監(jiān)聽(tīng)ChooseLevelScene的返回按鈕// 延時(shí)返回QTimer::singleShot(500, this, [=](){// 發(fā)送自定義信號(hào)emit this->chooseSceneBack();});});
在ChooseScene選擇關(guān)卡場(chǎng)景中,監(jiān)聽(tīng)PlayScene的返回信號(hào)
// chooselevelscene.cpp
// 返回從游戲場(chǎng)景返回到選關(guān)場(chǎng)景connect(play, &PlayScene::chooseSceneBack, this, [=](){this->show();delete play; // 刪除游戲場(chǎng)景,因?yàn)槊總€(gè)場(chǎng)景不一樣play = nullptr;});
5.4 顯示當(dāng)前關(guān)卡
// playscene.cpp
// 顯示當(dāng)前的關(guān)卡數(shù)QLabel *label = new QLabel;label->setParent(this);QFont font;font.setFamily("華文新魏");font.setPointSize(20);QString str1 = QString("Level: %1").arg(this->levelIndex);// 將字體設(shè)置到標(biāo)簽控件中l(wèi)abel->setFont(font);label->setText(str1);label->setGeometry(30, this->height() - 50, 120, 50);
?
5.5 創(chuàng)建金幣背景圖片
// playscene.cpp
// 顯示金幣背景圖案for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {// 繪制背景圖片QPixmap pix = QPixmap(":/res/BoardNode.png");QLabel *label = new QLabel;label->setGeometry(0, 0, pix.width(), pix.height());label->setPixmap(pix);label->setParent(this);label->move(97 + i * 50, 200 + j * 50);}}
5.6 創(chuàng)建金幣類
我們道,金幣是本游戲的核心對(duì)象,并且在游戲中可以利用二維數(shù)組進(jìn)行維護(hù),擁有支持點(diǎn)擊,翻瓣專特效等特殊性,因此不妨將金幣單獨(dú)封裝到一個(gè)類中,完成金幣所需的所有功能。
5.6.1 創(chuàng)建金幣類 MyCoin

并修改MyCoin的基類為QPushButton
5.6.2 構(gòu)造函數(shù)
在資源圖片中,我們可以看到,金幣翻轉(zhuǎn)的效果原理是多張圖片切換而形成的,而以下八張圖片中,第一張與最后一張比較特殊,因此我們?cè)诮o用戶看的時(shí)候,無(wú)非是金幣Coin0001 或者是銀幣Coin0008 這兩種圖。
因此我們?cè)趧?chuàng)建一個(gè)金幣對(duì)象時(shí)候,應(yīng)該提供一個(gè)參數(shù),代表著傳入的是金幣資源路徑還是銀幣資源路徑,根據(jù)路徑我們創(chuàng)建不同樣式的圖案
在MyCoin.h中聲明
// MyCoin.h
// 參數(shù)代表 傳入的金幣路徑 還是銀幣路徑MyCoin(QString btnImg);
在MyCoin.cpp中進(jìn)行實(shí)現(xiàn)
// Mycoin.cpp
MyCoin::MyCoin(QString btnImg) {QPixmap pix;bool ret = pix.load(btnImg);if (!ret) {QString str = QString("圖片 %1 加載失敗").arg(btnImg);qDebug() << str;return;}this->setFixedSize(pix.width(), pix.height());this->setStyleSheet("QPushButton{border:0px;}");this->setIcon(pix);this->setIconSize(QSize(pix.width(), pix.height()));
}
5.6.3測(cè)試
在翻金幣場(chǎng)景PlayScene中,我們測(cè)試下封裝的金幣類是否可用,可以在創(chuàng)建好的金幣背景代碼后,添加如下代碼:
// playscene.cpp
// 創(chuàng)建金幣MyCoin *coin = new MyCoin(":/res/Coin0001.png");coin->setParent(this);coin->move(99 + i * 50, 203 + j * 50);
運(yùn)行效果 :
<img
5.7引入關(guān)卡數(shù)據(jù)
當(dāng)然上述的測(cè)試只是為了讓我們失道提供的對(duì)外接口可行,但是每個(gè)關(guān)卡的初始化界面并非如此,因此需要我們引用一個(gè)現(xiàn)有的關(guān)卡文件,文件中記錄了各個(gè)關(guān)卡的金幣排列清空,也就是二維數(shù)組的數(shù)值。
5.7.1 添加現(xiàn)有文件dataConfig
首先先將dataConfig.h和dataConfig.cpp文件放入到當(dāng)前項(xiàng)目
5.7.2 添加現(xiàn)有文件
其次在Qt Creator項(xiàng)目右鍵,點(diǎn)擊添加現(xiàn)有文件
5.7.3 完成添加
選擇當(dāng)前項(xiàng)目下的文件,并進(jìn)行添加
5.7.4 數(shù)據(jù)分析
我們可以看到,其實(shí)dataConfig.h中只有一個(gè)數(shù)據(jù)是對(duì)外提供的,如下圖
// dataConfig.h
QMap<int, QVector< QVector<int> > > mData;
在上圖中,QMap<int,QVector<QVector<int>> mData
都記錄著每個(gè)關(guān)卡中的數(shù)據(jù)。其中,int代表對(duì)應(yīng)的關(guān)卡,也就是QMap中的key值,而value值就是對(duì)應(yīng)的二維數(shù)組,我們利用的是QVector< QVector<int> >
來(lái)記錄著其中的二維數(shù)。
5.7.5 測(cè)試關(guān)卡數(shù)據(jù)
在Main函數(shù)可以測(cè)試第一關(guān)的數(shù)據(jù),添加如下代碼:
// main.cpp
// 測(cè)試關(guān)卡數(shù)據(jù)dataConfig config;for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {qDebug() << config.mData[1][i][j];}qDebug() << "";}
對(duì)應(yīng)著dataConfig.cpp中第一關(guān)數(shù)據(jù)來(lái)看,與之匹配成功,以后我們就可以用dataConfig中的數(shù)據(jù)來(lái)對(duì)關(guān)卡進(jìn)行初始化了
5.8 初始化各個(gè)關(guān)卡
首先,可以在playScene中聲明一個(gè)成員變量,用戶記錄當(dāng)前關(guān)卡的二維數(shù)組
// playScene.h
int gameArray[4][4]; // 二維數(shù)組 維護(hù)每個(gè)關(guān)卡的具體數(shù)據(jù)
之后,在.cpp文件中,初始化這個(gè)二維數(shù)組
// playScene.cpp
// 初始化每個(gè)關(guān)卡的二維數(shù)組dataConfig config;for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {this->gameArray[i][j] = config.mData[this->levelIndex][i][j];}}
初始化成功后,在金幣類也就是Mycoin類中,擴(kuò)展屬性posX,posY,以及flag這三個(gè)屬性分別代表了,該金幣在二維數(shù)組中x的坐標(biāo),的坐標(biāo),以及當(dāng)前的正反標(biāo)志。
// mycoin.h// 金幣的屬性int posX; // x坐標(biāo)位置int posY; // y坐標(biāo)位置bool flag; // 正反標(biāo)志
然后完成金幣初始化,代碼如下:
// playscene.cpp// 創(chuàng)建金幣QString str;if (this->gameArray[i][j] == 1) {// 顯示金幣str = ":/res/Coin0001.png";}else {str = ":/res/Coin0008.png";}MyCoin *coin = new MyCoin(str);coin->setParent(this);coin->move(99 + i * 50, 203 + j * 50);// 給金幣的屬性賦值coin->posX = i;coin->posY = j;coin->flag = this->gameArray[i][j]; // 1正面 0反面
運(yùn)行測(cè)試各個(gè)關(guān)卡初始化,例如第一關(guān)效果如
5.9 翻金幣特效
5.9.1 MyCoin類擴(kuò)展屬性和行為
關(guān)卡的初始化完成后,下面就應(yīng)該點(diǎn)擊金幣,進(jìn)行翻轉(zhuǎn)的效果了,那么首先我們先在MyCoin類中創(chuàng)建出該方法。
在MyCoin.h中聲明:
// MyCoin.h
// 改變標(biāo)志的方法void changeFlag();QTimer *timer1; // 正面翻反面的定時(shí)器QTimer *timer2; // 反面翻正面的定時(shí)器int min = 1; // 最小圖片(正面)int max = 8; // 最大圖片(反面)
MyCoin.cpp中做實(shí)現(xiàn)
// MyCoin.cpp
// 改變正反面標(biāo)志的方法
void MyCoin::changeFlag() {// 如果是正面 翻成反面if (this->flag) {// 開(kāi)始正面翻反面的定時(shí)器timer1->start(30);this->flag = false;}else {this->flag = true;}
}
5.9.2 創(chuàng)建特效
當(dāng)我們分別啟動(dòng)兩個(gè)定時(shí)器時(shí),需要在構(gòu)造函數(shù)中做監(jiān)聽(tīng)操作,并且做出響應(yīng),翻轉(zhuǎn)金幣,然后再結(jié)束定時(shí)器。
構(gòu)造函數(shù)中進(jìn)行下列監(jiān)聽(tīng)代碼:
// mycoin.cpp
// 監(jiān)聽(tīng)正面翻反面的信號(hào) 并且翻轉(zhuǎn)金幣connect(timer1, &QTimer::timeout, this, [=](){QPixmap pix;QString str = QString(":/res/Coin000%1").arg(this->min++);pix.load(str);this->setFixedSize(pix.width(), pix.height());this->setStyleSheet("QPushButton{border:0px;}");this->setIcon(pix);this->setIconSize(QSize(pix.width(), pix.height()));if (this->min > this->max) {this->min = 1;timer1->stop();}});// 監(jiān)聽(tīng)正面翻反面的信號(hào) 并且翻轉(zhuǎn)金幣connect(timer2, &QTimer::timeout, this, [=](){QPixmap pix;QString str = QString(":/res/Coin000%1").arg(this->max--);pix.load(str);this->setFixedSize(pix.width(), pix.height());this->setStyleSheet("QPushButton{border:0px;}");this->setIcon(pix);this->setIconSize(QSize(pix.width(), pix.height()));if (this->max < this->min) {this->max = 8;timer2->stop();}});
5.9.3 禁用按鈕
此時(shí),確實(shí)已經(jīng)可以執(zhí)行翻辨轉(zhuǎn)金幣代碼了,但是如果快速點(diǎn)擊,會(huì)在金幣還沒(méi)有執(zhí)行一個(gè)完整動(dòng)作之后,又繼續(xù)開(kāi)始新的動(dòng)畫,我們應(yīng)該在金幣做動(dòng)畫期間,禁止再次點(diǎn)擊,并在完成動(dòng)畫后,開(kāi)啟點(diǎn)擊。
在MyCoin類中加入一個(gè)標(biāo)志isAnimation代表是否正在做需專動(dòng)畫,默認(rèn)isAnimation值為false。
// MyCoin.h
// 執(zhí)行動(dòng)畫的標(biāo)志bool isAnimation = false;
在MyCoin做動(dòng)畫期間加入
// MyCoin.cpp
isAnimation = true; // 開(kāi)始做動(dòng)畫
也就是changeFlag函數(shù)中將標(biāo)志設(shè)為true
加入位置如下
// 改變正反面標(biāo)志的方法
void MyCoin::changeFlag() {// 如果是正面 翻成反面if (this->flag) {// 開(kāi)始正面翻反面的定時(shí)器timer1->start(30);isAnimation = true; // 開(kāi)始做動(dòng)畫this->flag = false;}else {// 反面翻正面的定時(shí)器timer2->start(30);isAnimation = true; // 開(kāi)始做動(dòng)畫this->flag = true;}
}
并且在做完動(dòng)畫時(shí),將標(biāo)志改為false
重寫按鈕的按下事件,判斷如果正在執(zhí)行動(dòng)畫,那么直接return掉,不要執(zhí)行后續(xù)代碼。
代碼如下
// mycoin.cpp
void MyCoin::mousePressEvent(QMouseEvent *e) {if (this->isAnimation) {return;}else {QPushButton::mousePressEvent(e);}
}
5.9.4 翻周圍金幣
解決快速點(diǎn)擊的效果不好
- 在MyCoin中加入了isAnimation判斷是否正在做動(dòng)畫條件
- 當(dāng)按下MyCoin判斷是否在做動(dòng)畫,如果做動(dòng)畫,直接return保證金幣和銀幣動(dòng)態(tài)切換的完整效果
將用戶點(diǎn)擊的周圍上下左右4個(gè)金幣也進(jìn)行延時(shí)翻轉(zhuǎn),代碼寫到監(jiān)聽(tīng)點(diǎn)擊金幣下。此時(shí)我們發(fā)現(xiàn)還需要記錄住每個(gè)按鈕的內(nèi)容,所以我們將所有金幣按鈕也放到一個(gè)二維數(shù)組中,在.h中聲明
// playscene.h
MyCoin *coinBtn[4][4]; // 金幣按鈕數(shù)組
并且記錄每個(gè)按鈕的位置
// playscene.cpp
// 將金幣放入到 金幣的二維數(shù)組里 以便以后期的維護(hù)coinBtn[i][j] = coin;
翻轉(zhuǎn)周圍硬幣
// playscene.cpp
// 翻轉(zhuǎn)周圍硬幣// 延時(shí)翻轉(zhuǎn)QTimer::singleShot(300, this, [=](){// 周圍的右側(cè)金幣翻轉(zhuǎn)的條件if (coin->posX + 1 < 4) {coinBtn[coin->posX + 1][coin->posY]->changeFlag();this->gameArray[coin->posX + 1][coin->posY] = this->gameArray[coin->posX + 1][coin->posY] == 0 ? 1 : 0;}// 周圍的左側(cè)金幣翻轉(zhuǎn)的條件if (coin->posX - 1 > -1) {coinBtn[coin->posX - 1][coin->posY]->changeFlag();this->gameArray[coin->posX - 1][coin->posY] = this->gameArray[coin->posX - 1][coin->posY] == 0 ? 1 : 0;}// 周圍的下側(cè)金幣翻轉(zhuǎn)的條件if (coin->posY + 1 < 4) {coinBtn[coin->posX][coin->posY + 1]->changeFlag();this->gameArray[coin->posX][coin->posY + 1] = this->gameArray[coin->posX][coin->posY + 1] == 0 ? 1 : 0;}// 周圍的上側(cè)金幣翻轉(zhuǎn)的條件if (coin->posY - 1 > -1) {coinBtn[coin->posX][coin->posY - 1]->changeFlag();this->gameArray[coin->posX][coin->posY - 1] = this->gameArray[coin->posX][coin->posY - 1] == 0 ? 1 : 0;}});
5.10 判斷是否勝利
- PlayScene中添加isWin的標(biāo)志來(lái)判斷是否勝利
- 如果勝利了,打印勝利信息
- 將所有按鈕屏蔽掉點(diǎn)擊
在MyCoin.h中加入isWin標(biāo)志,代表是否勝利。
// MyCoin.h
// 是否勝利的標(biāo)志bool isWin;
默認(rèn)設(shè)置為true,只要有一個(gè)反面的金幣,就將該值改為false,視為未成功。代碼寫到延時(shí)翻金幣后進(jìn)行判斷
// 判斷游戲是否勝利this->isWin = true; // 默認(rèn)游戲勝利for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {if (!coinBtn[i][j]->flag) {isWin = false;break;}}}if (this->isWin) {// 勝利了// 將所有按鈕的勝利標(biāo)志改為truefor (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {coinBtn[i][j]->isWin = true;}}}
5.11 勝利圖片顯示
將勝利的圖片提前創(chuàng)建好,如果勝利觸發(fā)了,將圖片彈下來(lái)即可
// playscene.cpp
// 勝利圖片顯示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());
如果勝利了,將上面的圖片移動(dòng)下來(lái)
// 將勝利的圖片移動(dòng)下來(lái)QPropertyAnimation *animation = new QPropertyAnimation(winLabel, "geometry");// 設(shè)置時(shí)間間隔animation->setDuration(1000);// 設(shè)置開(kāi)始位置animation->setStartValue(QRect(winLabel->x(), winLabel->y(), winLabel->width(), winLabel->height()));// 設(shè)置結(jié)束位置animation->setEndValue(QRect(winLabel->x(), winLabel->y() + 114, winLabel->width(), winLabel->height()));// 設(shè)置緩和曲線 38valueanimation->setEasingCurve(QEasingCurve::OutBounce);// 執(zhí)行動(dòng)畫animation->start();
5.12 勝利后禁用按鈕
當(dāng)勝利后,應(yīng)該禁用所有按鈕的點(diǎn)擊狀態(tài),可以在每個(gè)按鈕中加入標(biāo)志位isWin,如果isWin為true,MousePressEvent直接return掉即可
MyCoin.h中里添加:
// MyCoin.h
// 是否勝利的標(biāo)志bool isWin = false; // 要設(shè)置false 否則系統(tǒng)隨機(jī)分配可能無(wú)法觸發(fā)鼠標(biāo)事件
在鼠標(biāo)按下事件中修改為
// MyCoin.cpp
void MyCoin::mousePressEvent(QMouseEvent *e) {if (this->isAnimation || this->isWin) {return;}else {QPushButton::mousePressEvent(e);}
}
勝利后不可以點(diǎn)擊任何的金幣。
6 音效添加
Cmake添加模塊
6.1 開(kāi)始音效
// mainscene.cpp
// 準(zhǔn)備開(kāi)始按鈕的音效QSoundEffect *startSound = new QSoundEffect(this);// 設(shè)置聲音源文件的路徑startSound->setSource(QUrl::fromLocalFile(":/res/TapButtonSound.wav"));// 音頻循環(huán)的次數(shù)startSound->setLoopCount(1);// 音量startSound->setVolume(1);
點(diǎn)擊開(kāi)始按鈕,播放音效
// mainscene.cpp
// 播放開(kāi)始音效資源startSound->play();
6.2 選擇關(guān)卡音效
在選擇關(guān)卡場(chǎng)景中,添加音效
// chooselevelscene.cpp
// 選擇關(guān)卡音效QSoundEffect *chooseSound = new QSoundEffect(this);// 設(shè)置聲音源文件的路徑chooseSound->setSource(QUrl::fromLocalFile(":/res/TapButtonSound.wav"));// 音頻循環(huán)的次數(shù)chooseSound->setLoopCount(1);// 音量chooseSound->setVolume(1);
選中關(guān)卡后,播放音效
// chooselevelscene.cpp
// 播放選擇關(guān)卡音效chooseSound->play();
6.3 返回按鈕音效
在選擇關(guān)卡場(chǎng)景與翻金幣游戲場(chǎng)景中,分別添加返回按鈕音效如下
// chooselevelscene.cpp
// 返回按鈕音效QSoundEffect *backSound = new QSoundEffect(this);// 設(shè)置聲音源文件的路徑backSound->setSource(QUrl::fromLocalFile(":/res/BackButtonSound.wav"));// 音頻循環(huán)的次數(shù)backSound->setLoopCount(1);// 音量backSound->setVolume(1);
分別在點(diǎn)擊返回按鈕后,播放該音效
// chooselevelscene.cpp
// 播放返回按鈕音效backSound->play();
6.4 翻金幣與勝利音效
在PlayScene中添加,翻金幣的音效以及勝利的音效
// PlayScene.cpp
// 翻金幣音效QSoundEffect *flipSound = new QSoundEffect(this);// 設(shè)置聲音源文件的路徑flipSound->setSource(QUrl::fromLocalFile(":/res/ConFlipSound.wav"));// 音頻循環(huán)的次數(shù)flipSound->setLoopCount(1);// 音量flipSound->setVolume(1);// 勝利按鈕音效QSoundEffect *winSound = new QSoundEffect(this);// 設(shè)置聲音源文件的路徑winSound->setSource(QUrl::fromLocalFile(":/res/LevelWinSound.wav"));// 音頻循環(huán)的次數(shù)winSound->setLoopCount(1);// 音量winSound->setVolume(1);
在翻金幣時(shí)播放翻金幣音效
// PlayScene.cpp
// 播放翻金幣的音效flipSound->play();
勝利時(shí),播放勝利音效
// PlayScene.cpp
// 添加勝利的音效winSound->play();
測(cè)試音效,使音效正常播放
7 優(yōu)化項(xiàng)目
當(dāng)我們移動(dòng)場(chǎng)景后,如果進(jìn)入下一個(gè)場(chǎng)景,發(fā)現(xiàn)場(chǎng)景還在中心位置,如果想設(shè)置場(chǎng)景的位置,需要添加如下下圖中的代碼:
MainScene中添加:
// MainScene.cpp
this->setGeometry(chooseScene->geometry());chooseScene->setGeometry(this->geometry());
ChooseScene中添加
// ChooselevelScene.cpp
// 設(shè)置游戲場(chǎng)景的初始位置play->setGeometry(this->geometry());this->setGeometry(play->geometry());
8 項(xiàng)目打包
**第一步 **
第二步
第三步
第四步 還有要確保第三步要加入到系統(tǒng)環(huán)境變量中否則無(wú)法執(zhí)行第四步