QT學習例程1—翻金幣教程(教學視訊連結:https://www.bilibili.com/video/BV1g4411H78N?p=61)
1、專案簡介
如圖所示,將所有的金幣都翻轉為金色,即可取得勝利。
2、專案基本配置
- 建立專案(注意不要有中文路徑),類名為MainScene
- 新建Qt Resource File檔案,新增圖示和音樂等檔案到工程專案下
3、主場景
3.1 整個工程專案包括三個場景:
- 主場景:開始介面(maincene.cpp)
- 選擇關卡場景:進行關卡的選擇(chooselevelscene.cpp)
- 翻金幣場景:遊戲的主要場景(playscene.cpp)
3.2 在上一步中,我們已經新建了MainScene的類,下面說一下MainScene需要做的工作:
(1)場景的基本配置
- 設定固定大小(this->setFixedSize(320,588))
- 設定應用圖示(this->setWindowIcon(QPixmap(":/res/Coin0001.png")))
- 設定視窗標題(this->setWindowTitle("翻金幣"))
- 設定背景圖片(需要重寫MainScene的PaintEvent事件,記得在標頭檔案宣告一下)
void MainScene::paintEvent(QPaintEvent *) { //建立畫家,指定繪圖裝置 QPainter painter(this); //建立QPixmap物件 QPixmap pix; //載入圖片 pix.load(":/res/PlayLevelSceneBg.png"); //繪製背景圖 painter.drawPixmap(0,0,this->width(),this->height(),pix); //載入標題 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); }
(2)建立開始按鈕(實現彈跳效果,需要封裝出一個按鈕控制元件,來實現這些效果)
- 新建MyPushButton類,記得修改標頭檔案程式碼繼承於QPushButton
- 修改MyPushButton的標頭檔案:提供構造的過載版本,可以讓MyPushButton提供正常顯示的圖片及按下後顯示的圖片;同時需要定義按鈕向上和向下跳的特效,需要定義void zoom1( );和void zoom2( );以及需要寫一下滑鼠按下和釋放的事件;
//normalImg 代表正常顯示的圖片 //pressImg 代表按下後顯示的圖片,預設為空 MyPushButton(QString normalImg,QString pressImg = ""); QString normalImgPath; //預設顯示圖片路徑 QString pressedImgPath; //按下後顯示圖片路徑
void zoom1();//向下跳
void zoom2();//向上跳
//重寫按下和釋放事件
void mousePressEvent(QMouseEvent *);
void mouseReleaseEvent(QMouseEvent *);
- 編寫MyPushButton.cpp的程式碼(就是在標頭檔案中宣告的那幾個函式)
MyPushButton::MyPushButton(QString normalImg,QString pressImg) { //成員變數normalImgPath儲存正常顯示圖片路徑 normalImgPath = normalImg; //成員變數pressedImgPath儲存按下後顯示的圖片 pressedImgPath = pressImg; //建立QPixmap物件 QPixmap pixmap; //判斷是否能夠載入正常顯示的圖片,若不能提示載入失敗 bool ret = pixmap.load(normalImgPath); if(!ret) { qDebug() << normalImg << "載入圖片失敗!"; } //設定圖片的固定尺寸 this->setFixedSize( pixmap.width(), pixmap.height() ); //設定不規則圖片的樣式表 this->setStyleSheet("QPushButton{border:0px;}"); //設定圖示 this->setIcon(pixmap); //設定圖示大小 this->setIconSize(QSize(pixmap.width(),pixmap.height())); } void MyPushButton::zoom1() { //建立動畫物件 QPropertyAnimation * animation1 = new QPropertyAnimation(this, "geometry"); //設定時間間隔,單位毫秒 animation1->setDuration(200); //建立起始位置 animation1->setStartValue(QRect(this->x(), this->y(), this->width(), this->height())); //建立結束位置 animation1->setEndValue(QRect(this->x(), this->y(), this->width(), this-> height())); //設定緩和曲線,QEasingCurve::OutBounce為彈跳效果 animation1->setEasingCurve(QEasingCurve::OutBounce); //開始執行動畫 animation1->start(); } void MyPushButton::zoom2() { QPropertyAnimation * animation1 = new QPropertyAnimation(this, "geometry"); animation1->setDuration(200); animation1->setStartValue(QRect(this->x(), this->y()+10, this->width(), this->height())); animation1->setStartValue(QRect(this->x(), this->y(), this->width(), this->height())); animation1->setEasingCurve(QEasingCurve::OutBounce); animation1->start(); } //滑鼠按下事件 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())); } //交給父類執行按下事件 return QPushButton::mousePressEvent(e); } //滑鼠釋放事件 void MyPushButton::mouseReleaseEvent(QMouseEvent *e) { if(normallImgPath != "")//選中路徑不為空,顯示選中圖片 { QPixmap pixmap; bool ret = pixmap.load(normallImgPath); if(!ret) { qDebug()<<normallImgPath<<"載入圖片失敗"; } this->setFixedSize(pixmap.width(), pixmap.height()); this->setStyleSheet("QPushButton{border:0px}"); this->setIcon(pixmap); this->setIconSize(QSize(pixmap.width(), pixmap.height())); } //交給父類執行,釋放事件 return QPushButton::mouseReleaseEvent(e); }
- 在MainScene的建構函式中,建立開始按鈕,同時監聽點選按鈕事件,執行特效,進入選擇關卡場景
MyPushButton * startBtn = new MyPushButton(":/res/MenuSceneStartButton.png"); startBtn->setParent(this); startBtn->move(this->width()*0.5-startBtn->width()*0.5,this->height()*0.7);
//監聽點選事件,執行特效
connect(startBtn, &MyPushButton::clicked, [=](){
//播放開始的音效資源
startSound->play(); //開始音效
startBtn->zoom1();//向下跳躍
startBtn->zoom2();//向上跳躍
//進入選擇關卡場景
//延時0.5秒後,進入選擇場景
QTimer::singleShot(1000, this,[=](){
this->hide();
chooseScene->show();
});
});
- 上述步驟的執行效果如圖所示:
4、選擇關卡場景
4.1 選擇關卡的設定主要包括以下幾個方面
- 場景的基本設定(包括固定的大小、標題之類的)
- 按鈕功能設定(返回按鈕和關卡選擇按鈕)
4.2 詳細說一下選擇關卡的設定
(1)場景基本設定及背景設定
//設定視窗固定大小 this->setFixedSize(320,588); //設定圖示 this->setWindowIcon(QPixmap(":/res/Coin0001.png")); //設定標題 this->setWindowTitle("選擇關卡"); //建立選單欄 QMenuBar * bar = this->menuBar(); this->setMenuBar(bar); //建立開始選單 QMenu * startMenu = bar->addMenu("開始"); //建立按鈕選單項 QAction * quitAction = startMenu->addAction("退出"); //點選退出 退出遊戲 connect(quitAction,&QAction::triggered,[=](){this->close();}); void ChooseLevelScene::paintEvent(QPaintEvent *) { QPainter painter(this); QPixmap pix; pix.load(":/res/OtherSceneBg.png"); painter.drawPixmap(0,0,this->width(),this->height(),pix); //載入標題 pix.load(":/res/Title.png"); painter.drawPixmap( (this->width() - pix.width())*0.5,30,pix.width(),pix.height(),pix); }
(2)返回按鈕設定,點選之後返回開始主介面,所有的按鈕功能都是呼叫的MyPushButton中滑鼠按下和彈起的功能,由於返回按鈕有正常顯示圖片和點選後顯示圖片兩種模式,所有需要重寫MyPushButton.cpp中的MousePressEvent和MouseReleaseEvent(3.2—(2)的那個程式碼已經是修改過的,可以直接使用),同時音效實現的功能也直接在這裡添加了(注意如果要使用音效的話,需要在工程檔案程式碼中第一句加上multimedia,即QT += core gui multimedia,這樣就可以在標頭檔案中找到 # include <QSound.h>了)
//返回按鈕音效
QSound *backSound = new QSound(":/res/BackButtonSound.wav",this);
//返回按鈕 MyPushButton * closeBtn = new MyPushButton(":/res/BackButton.png", ":/res/BackButtonSelected.png"); closeBtn->setParent(this); closeBtn->move(this->width()-closeBtn->width(), this->height()-closeBtn->height()); //返回按鈕功能實現 connect(closeBtn, &MyPushButton::clicked, [=](){ //播放返回音效 backSound->play(); QTimer::singleShot(500, this,[=](){ this->hide(); //觸發自定義訊號,關閉自身,該訊號寫到signals下做宣告 emit this->chooseSceneBack(); }); });
(3)選擇關卡按鈕設定(同樣加入了音效的功能)
//選擇關卡按鈕音效 QSound *chooseSound = new QSound(":/res/TapButtonSound.wav",this); //建立選擇關卡按鈕 for(int i = 0; i < 20; i++) { MyPushButton * menuBtn = new MyPushButton(":/res/LevelIcon.png"); menuBtn->setParent(this); menuBtn->move(130 + (i % 4) * 100, 200 + (i / 4) * 120); //監聽每個按鈕的點選事件 connect(menuBtn, &MyPushButton::clicked, [=](){ //播放選擇關卡的音效 chooseSound->play(); QString str = QString("您選擇的是第%1關").arg(i+1); qDebug() << str; //進入到遊戲場景 this->hide();//將選關場景隱藏掉 play = new PlayScene(i+1);//建立遊戲場景 play->show();//顯示遊戲場景 connect(play, &PlayScene::chooseSceneBack, [=](){ this->show(); delete play; play = NULL; }); }); //按鈕上顯示的文字 QLabel* label = new QLabel; label->setParent(this); label->setFixedSize(menuBtn->width(), menuBtn->height()); label->setText(QString::number(i + 1)); label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);//設定居中 label->move(130+ (i % 4) * 100, 300 + (i / 4) * 100); label->setAttribute(Qt::WA_TransparentForMouseEvents, true);//滑鼠事件穿透 } }
5、翻金幣場景
5.1翻金幣場景的設定主要包括以下幾個方面
- 場景及背景的設定(視窗大小、圖示、選單欄)、背景設定、當前關卡文字顯示、金幣背景顯示
- 返回按鈕設定
- 建立金幣類
5.2 詳細的內容介紹如下:
(1)場景及背景的設定(視窗大小、圖示、選單欄)、背景設定、當前關卡文字顯示、金幣背景顯示
- 標頭檔案宣告
public: explicit PlayScene(QWidget *parent = nullptr); PlayScene(int index); //成員變數 記錄關卡索引 int levelIndex; //背景函式 void paintEvent(QPaintEvent *); //宣告一個成員變數 int gameArray[4][4];//二維陣列資料 //金幣按鈕陣列 MyCoin * coinBtn[4][4]; //判斷是否勝利 bool isWin; QMediaPlayer *endPlayer; signals: void chooseSceneBack();
- 場景設定
PlayScene::PlayScene(int index) { //qDebug() << "當前關卡為"<< index; this->levalIndex = index; //設定視窗固定大小 this->setFixedSize(320,588); //設定圖示 this->setWindowIcon(QPixmap(":/res/Coin0001.png")); //設定標題 this->setWindowTitle("翻金幣"); //建立選單欄 QMenuBar * bar = this->menuBar(); this->setMenuBar(bar); //建立開始選單 QMenu * startMenu = bar->addMenu("開始"); //建立按鈕選單項 QAction * quitAction = startMenu->addAction("退出"); //點選退出 退出遊戲 connect(quitAction,&QAction::triggered,[=](){this->close();}); }
- 背景設定
void PlayScene::paintEvent(QPaintEvent *) { //載入背景 QPainter painter(this); QPixmap pix; pix.load(":/res/PlayLevelSceneBg.png"); painter.drawPixmap(0,0,this->width(),this->height(),pix); //載入標題 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); }
- 當前關卡顯示
//當前關卡標題 QLabel * label = new QLabel; label->setParent(this); QFont font; font.setFamily("華文新魏"); font.setPointSize(20); label->setFont(font); QString str = QString("Leavel: %1").arg(this->levalIndex); label->setText(str); label->setGeometry(QRect(30, this->height() - 50,120, 50)); //設定大小和位置
- 建立金幣背景圖片、金幣及相關屬性(都是在playscene.c中實現的)
//繪製背景圖片 QPixmap pix=QPixmap(":/res/BoardNode(1).png"); QLabel *label=new QLabel; label->setGeometry(0,0,50,50); label->setPixmap(pix); label->setParent(this); // label->move(57+i*50,200+j*50); label->move(150 + i * 80, 300 + j * 80); //建立金幣 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(59+i*50,204+j*50); coin->move(150 + i * 80, 300 + j * 80); //給金幣的屬性賦值 coin->posX=i; coin->posY=j; coin->flag=this->gameArray[i][j]; // 1正面 0反面 //將金幣放入到金幣的二維數組裡面 以便於後期的維護 coinBtn[i][j]=coin;
(2)返回按鈕設定
//返回按鈕 MyPushButton * closeBtn = new MyPushButton(":/res/BackButton.png",":/res/BackButtonSelected.png"); closeBtn->setParent(this); closeBtn->move(this->width()-closeBtn->width(),this->height()-closeBtn->height()); //返回按鈕功能實現 connect(closeBtn,&MyPushButton::clicked,[=](){ QTimer::singleShot(500, this,[=](){ this->hide(); //觸發自定義訊號,關閉自身,該訊號寫到 signals下做宣告 emit this->chooseSceneBack(); } ); });
(3)建立金幣類:利用二維陣列對金幣屬性進行維護,且支援點選、翻轉特效,把這些功能進行封裝
- 標頭檔案宣告
public: // explicit MyCoin(QWidget *parent = nullptr); //圖片路徑 MyCoin(QString btnImg); //擴充套件金幣類的屬性 int posX;//x座標 int posY;//y座標 bool flag;//正反標誌 //改變標誌,執行翻轉效果 void changeFlag(); QTimer * timer1;//正面翻反面 定時器 QTimer * timer2;//正面翻正面 定時器 int min = 1;//最小圖片 int max = 8;//最大圖片 //翻轉動畫的標誌 bool isAnimation = false; //重寫按鈕的按下事件 void mousePressEvent(QMouseEvent *); //勝利標誌 bool isWin = false;//勝利標誌
- 建構函式:建立金幣物件—提供一個引數—代表傳入的是金幣還是銀幣資源,根據路徑建立不同的圖案
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())); }
- 在當前的工程檔案中新增dataconfig.c和dataconfig.h檔案
- 初始化各個關卡
//初始化二維陣列 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.c)
//初始化定時器物件 time1=new QTimer(this); time2=new QTimer(this); //監聽正面翻反面的訊號, 並且翻硬幣 connect(time1,&QTimer::timeout,[=](){ 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())); //判斷如果翻完了 將min 重置為1 if(this->min>this->max) { this->min=1; isAnimation=false;//停止做動畫,禁用按鈕(當一個金幣在做翻轉動作時,另外一個動畫不能動)
time1->stop(); } }); //監聽反面翻正面的訊號,並且翻硬幣 connect(time2,&QTimer::timeout,[=](){ 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())); //判斷如果翻完了 將min 重置為1 if(this->max<this->min) { this->max=8; isAnimation=false;//停止做動畫 time2->stop(); }
void MyCoin::changeFlag() { //如果是正面 翻成反面 if(this->flag) { time1->start(30); isAnimation=true;//開始做動畫 this->flag=false; } else{//反面翻正面 time2->start(30); isAnimation=true;//開始做動畫 this->flag=true; }
- 翻周圍硬幣(上下左右四個硬幣同時翻動,playscene.c程式碼)
//顯示金幣背景圖案 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(57 + i * 50, 200 + j * 50); //初始化金幣物件 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(150 + i * 80, 300 + j * 80); coin->posX = i;//記錄x座標 coin->posY = j;//記錄y座標 coin->flag = gameArray[i][j]; //記錄每個按鈕的位置 coinBtn[i][j] = coin; //測試翻轉金幣的效果 connect(coin, &MyCoin::clicked, [=](){ flipSound->play(); coin->changeFlag(); //陣列內部記錄的標誌同步修改 gameArray[i][j] = gameArray[i][j] == 0 ? 1 : 0; //翻轉周圍金幣 QTimer::singleShot(300, this, [=](){ 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; } 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; } if(coin->posY + 1 <= 3) { coinBtn[coin->posX][coin->posY + 1]->changeFlag(); gameArray[coin->posX][coin->posY + 1] = gameArray[coin->posX][coin->posY + 1] == 0 ? 1 :0; } if(coin->posY + 1 >= 0) { coinBtn[coin->posX][coin->posY - 1]->changeFlag(); gameArray[coin->posX][coin->posY - 1] = gameArray[coin->posX][coin->posY - 1] == 0 ? 1 : 0; } this->isWin = true; for(int i = 0; i < 4; i++) { for(int j = 0; j < 4; j++) { if(coinBtn[i][j]->flag==false) { this->isWin = false; break; } } } if(this->isWin == true) { winSound->play(); qDebug() << "遊戲勝利"; endPlayer->setMedia(QUrl::fromLocalFile("F:/QT/Day4/CoinFip/res/bkmusic.mp3")); endPlayer->setMedia(QUrl("qrc:/res/bkmusic.mp3")); endPlayer->setVolume(100); endPlayer->play(); //禁用所有按鈕點選事件 for(int i = 0 ; i < 4;i++) { for(int j = 0 ; j < 4; j++) { coinBtn[i][j]->isWin = true; } } //將勝利的圖片移動下來 QPropertyAnimation *animation=new QPropertyAnimation(winLabel,"geometry"); //設定時間間隔 animation->setDuration(1000); //設定開始位置 animation->setStartValue(QRect(QPoint(winLabel->x(),winLabel->y()),QPoint(winLabel->x()+winLabel->width(),winLabel->height()))); //設定結束位置 animation->setEndValue(QRect(QPoint(winLabel->x(),winLabel->y()+120),QPoint(winLabel->x()+winLabel->width(),winLabel->height()+120))); //設定緩和曲線 animation->setEasingCurve(QEasingCurve::OutBounce); //執行動畫 animation->start(); } }); }); } } }
- 勝利圖片顯示(playscene.c程式碼)
//勝利圖片顯示 QLabel *winLabel = new QLabel; QPixmap tmpPix; tmpPix.load(":/res/LevelCompletedDialogBg.png"); winLabel->setParent(this); winLabel->setGeometry(0, 0, tmpPix.width(), tmpPix.height()); winLabel->setPixmap(tmpPix); winLabel->move((this->width() - tmpPix.width()) * 0.5, -tmpPix.height());
注意事項:
(1)播放音樂的功能(記得在標頭檔案中宣告 #include <QSound>)
endPlayer=new QMediaPlayer(this);
endPlayer->setMedia(QUrl::fromLocalFile("F:/QT/Config/res/bkmusic.mp3"));
endPlayer->setMedia(QUrl("qrc:/res/bkmusic.mp3"));
endPlayer->setVolume(100);
endPlayer->play();
(2)金幣就類似按鈕的類,點選金幣就相當於點選按鈕(在其標頭檔案中已經把其父類改成了QPushButton)