QT actual combat gold coin game [detailed process and introduction]

Table of contents

Preface

1. Overall analysis of the game

2. Create a project

3. Add resources

4. Main interface implementation

1. Set the basic configuration of the main scene of the game

2. Set background image

3. Create a start button and set animation

4. Create a level selection interface and switch between the main interface and it

5. Implementation of level selection interface

1. Set the basic configuration of the level selection scene

2. Set the background image of the level selection scene

3. Related function settings of the return button

4. Create a select level button

5. Realize the function of clicking the level button to jump to the corresponding gold coin flipping scene.

6. Realization of gold coin flipping scene

1. Set the basic configuration of the game scene

2. Set the background image of the game scene

3. Set the return button and its function

4. Display the current level number in the lower left corner

5. Create a gold coin background in the middle of the game interface

6. Create gold coin category

7. Introduce level data

8. Initialize each level

9. Realize the special effect of flipping gold coins

10. Realize the function of flipping surrounding gold coins

11. Determine whether victory is achieved

12. Display victory picture

13. Disable button after victory

7. Add sound effects

8. Optimization projects

1. The entry and return of the three scenes are all at the same position.

2. Clicking on other gold coins at the moment of victory in the game resulted in failure to win (too fast)

9. Packaging Project


Preface

After simply learning the relevant knowledge and controls of QT, you can consolidate and practice the learned QT knowledge by implementing the " gold coin flipping game ". The production process is based on the tutorial in the following video. If you are interested, you can watch the video.

02 Case Introduction_bilibili_bilibili

The pictures and audio for making the gold coin flipping game are all directly made use of the existing materials provided in the course. If you cannot find them in the course, you can download the corresponding materials through the following link.

Link: https://pan.baidu.com/s/1IxWIa47V0L_WuLu41pu7Tw 
Extraction code: lje1

This article will introduce the implementation of the "CoinFlip" game step by step from beginning to end in a relatively detailed manner, mainly from the aspects of overall analysis, project creation, project implementation, and project packaging .

The packaged game link is as follows. You can download it directly and run it to play.

Link: https://pan.baidu.com/s/1Nfzstm1AbsmwfKPL_ac4_Q 
Extraction code: rqa6

Add the header file you created using the following form

#include "xxxx.h"


1. Overall analysis of the game

Overall, this small game requires us to implement three interfaces and two custom controls .

       The first is the game start interface , corresponding to Figure 1; among them, the start interface requires a start button to switch pages, so we need to customize a control, which is MyPushButton, and the parent class is QPushButton to realize the design and action of this button accomplish.

        The second is the level selection interface , corresponding to Figure 2; among them, the level button and the back button are also implemented using our custom controls.

        The third is the level interface , corresponding to Figure 3. Among them, because the game rule is to click on the silver coin and then flip the up, down, left, and right coins accordingly, multiple pictures need to be used to flip the gold coin, so we need to create a gold coin class to implement the corresponding operations required for the gold coin.

        Figure 4 only pops up a success picture based on the level interface after the player wins, so there is no need to create another interface. The three interfaces correspond to three classes, namely mainscene, chooselevelscene, and playscene; there are two custom classes, namely mypushbutton and mycoin classes.


2. Create a project

Open QT Creator (I am using version 5.12.9)-->Create a new project-->Select Qt Widgets Application-->Next step

Set the project name "CoinFlip"-->Select the storage path-->Next step

Start the implementation from the main interface, set the class name to "MainScene" --> select the parent class as "QMainWindow" --> check "Generate form" --> Next step

Then the project is created successfully and the project structure is as follows:


3. Add resources

Add the following resources to the project

Download and save the information prepared in the preface to the computer desktop-->right-click the project and select "Add New"-->select "QT"-->select "QT Resource File"-->Next step-->Set name" res"-->Next step-->Complete

After clicking Finish, the following interface will appear. Click "Add Prefix" --> set the prefix to "/".

(The test is to facilitate operations such as taking screenshots and adding resources, so don’t worry about this)

Then copy and paste the res folder in the previously saved data into the CoinFlip project (that is, the path you selected when creating the project earlier) [Ignore test, it is not the point]

Return to the project interface, click "Add Files" --> select the "res" file just added --> select all files in the folder --> click Open --> successfully add resources

After successful addition, it is as follows:


4. Main interface implementation

1. Set the basic configuration of the main scene of the game

First, we use UI interface design to realize the design of the menu bar - double-click "mainscene.ui" to open the interface design - the interface is as follows

Design the menu bar - enter "Start" in the upper middle "Enter here", then click "Start", enter "Quit" in the lower "Enter here", then find its text attribute and change it to Exit .

[The reason why Quit is entered first is because the control name is automatically generated, and "action exit" cannot always be generated]

Then remove the built-in status bar, then you can return to "mainscene.cpp" and write code in the constructor

Implement the basic configuration of the scene, set the window size, application icon, and window title, and then implement the exit function using signals and lambda expressions.

code show as below:

//设置固定大小
this->setFixedSize(350,550);
//设置应用图片
this->setWindowIcon(QPixmap(":/res/Coin0001.png"));
//设置窗口标题
this->setWindowTitle("一起来翻金币吧");
//点击退出,退出程序
connect(ui->actionQuit,&QAction::triggered,[=](){
    this->close();
});

The results of running the interface are as follows:

2. Set background image

You need to rewrite the PaintEvent event of MainScene. First add a statement in "mainscene.h", and then add the background image in "mainscene.cpp".

[Note: The PaintEvent function will be automatically called and generated when the project is running, so it only needs to be rewritten and does not need to be called in cpp]

code show as below:

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);

}

The results of running the interface are as follows:

3. Create a start button and set animation

      The following scenario needs to be implemented - the start button has a bouncing effect after being clicked

We use custom controls to achieve this effect (because QPushButton does not come with such special effects). We can encapsulate a button control ourselves to achieve these effects. At this point we need to create a new class - MyPushButton

     Right-click the project and select "Add New" --> select "C++" --> select "C++ Class" --> Next step --> set the class name "MyPushButton" --> select the parent class "QWidget" --> Next step --> Complete

After successful creation, the project structure is as follows:

Open "mypushbutton.h" and "mypushbutton.cpp" respectively and modify their inherited parent class to QPushButton.

The modification is as follows:

We hope that after clicking the button, we can see the change of the button, that is, let MyPushButton provide the picture displayed normally and the picture displayed after pressing, such as jumping up and down or becoming pressed, so we need to rewrite the structure of the MyPushButton class. function so that it can accept two parameters, which are the path of the picture displayed normally and the path of the picture to be displayed after pressing.

First declare the overloaded constructor and define two member variables in the mypushbutton.h file, which are used to store the paths of the corresponding images.

code show as below:

    //normalImg 代表正常显示的图片
    //pressImg  代表按下后显示的图片,默认为空
    MyPushButton(QString normalImg,QString pressImg = "");
private:
    QString normalImgPath;  //默认显示图片路径
    QString pressedImgPath; //按下后显示图片路径

Then the specific implementation code of the constructor is rewritten in "mypushbutton.cpp"

code show as below:

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()));
}

After completing the first two steps, we can add a start button to the main interface and open "mainscene.cpp"

code show as below:

//利用自定义控件定义开始按钮,同时设置其图标  这里就运用到了重载的构造函数
MyPushButton * startBtn = new MyPushButton(":/res/MenuSceneStartButton.png");
//将按钮的父亲设置为this,也就是添加到对象树上,这样当窗口析构时其也会被析构
startBtn->setParent(this);
//设置开始按钮在主界面的位置 也就是x,y
startBtn->move(this->width()*0.5-startBtn->width()*0.5,this->height()*0.7);

The results of running the interface are as follows:

At this time, there is no special effect when clicking the start button, so we need to add the special effect of jumping up and down after clicking. We define two functions zoom1() and zoom2(), which are used to realize downward jump and upward jump respectively. These two functions are placed in Implemented in mypushbutton class.

First add a statement in mypushbutton.h

Add the implementation code to mypushbutton.cpp. The two function codes are basically the same, only the location is different.

code show as below:

//向下跳跃
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()+10,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->setEndValue(QRect(this->x(),this->y(),this->width(),this->height()));
    //设置缓和曲线,QEasingCurve::OutBounce 为弹跳效果
    animation1->setEasingCurve(QEasingCurve::OutBounce);
    //开始执行动画
    animation1->start();
}

Then connect the signal slot to "mainscene.cpp" to achieve the effect of the button jumping up and down after clicking the start button.

code show as below:

//监听点击事件,执行特效
connect(startBtn,&MyPushButton::clicked,[=](){
     startBtn->zoom1(); //向下跳跃
     startBtn->zoom2(); //向上跳跃
});

At this point, click to run the project and click the start button to see the effect of jumping up and down.

[At this point, the creation of the main scene has been basically realized. The next step is to create the second interface - level selection, and at the same time, click the start button to jump to the level selection interface]

4. Create a level selection interface and switch between the main interface and it

Just like creating a custom control, we need to create a new class - "ChooseLevelScene" when creating a level selection interface.

  Right-click the project and select "Add New" --> select "C++" --> select "C++ Class" --> Next step --> set the class name "ChooseLevelScene" --> select the parent class "QMainWindow" --> Next step --> Complete

After successful creation, the project structure is as follows:

Then it is to implement the function of clicking the start button to enter the selected level scene. Therefore, we need to add the level scene object in "mainscene.h" to achieve interface switching through "chooseScene ->show()" after clicking the button .

code show as below:

//选择关卡场景
ChooseLevelScene *chooseScene = new ChooseLevelScene;

Then add code to the lambda expression after clicking the start button in "mainscene.cpp" to implement interface switching. Just add it after calling zoom2().

code show as below:

//延时0.5秒后 进入选择场景
QTimer::singleShot(500, this,[=](){
      this->hide();
      chooseScene->show();
});

Run the program and delay 0.5 seconds after executing the special effects to enter the level selection scene. The results are as follows: (Because the level selection interface has not been set yet, it is a blank interface)


5. Implementation of level selection interface

1. Set the basic configuration of the level selection scene

Directly set the interface configuration, such as size, menu bar, etc., in the constructor in "chooselevelscene.cpp"

code show as below:

//设置窗口固定大小
this->setFixedSize(350,550);
//设置图标
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();});

The running results at this time are as follows:

2. Set the background image of the level selection scene

Like the main scene, you need to rewrite the paintEvent function, first declare it in "chooselevelscene.h", and then rewrite it in "chooselevelscene.cpp"

code show as below:

void ChooseLevelScene::paintEvent(QPaintEvent *)
{
    //创建画家,制定绘图设备
    QPainter painter(this);
    //创建QPixmap对象
    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);
}

After the background image is set, let's set the return button in the lower right corner and use the custom control "mypushbutton" in " chooselevelscene.cpp " to create the return button.

code show as below:

//返回按钮
    MyPushButton * closeBtn = new MyPushButton(":/res/BackButton.png",":/res/BackButtonSelected.png");
    closeBtn->setParent(this);
    //设置按钮位置
    closeBtn->move(this->width()-closeBtn->width(),this->height()-closeBtn->height());

Because the return button has two modes: normal display of pictures and display of pictures after clicking, we need to rewrite the MousePressEvent and MouseReleaseEvent in MyPushButton to achieve different display effects.

First declare it in "mypushbutton.h" and then implement it in "mypushbutton.cpp"

code show as below:

//鼠标事件
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(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()));
    }
    //交给父类执行 释放事件
    return QPushButton::mouseReleaseEvent(e);
}

Run the interface and the results are as follows:

3. Related function settings of the return button

After we click the return button, we set a delay of 0.5 seconds to hide ourselves, and send a custom signal "chooseSceneBack () " to tell the outside world that we have chosen the return button.

We first declare the custom signal in "chooselevelscene.h" (but no implementation is required)

Then add the function of the return button in "chooselevelscene.cpp":

code show as below:

//返回按钮功能实现
connect(closeBtn,&MyPushButton::clicked,[=](){
    QTimer::singleShot(500, this,[=](){
       this->hide();
       //触发自定义信号,关闭自身
       emit this->chooseSceneBack();
    }
  );
});

In the main scene MainScene, we add a function - when clicking the start button to display the selected level, monitor the return button message of the selected level. If the return button message is received, the main scene interface will be displayed. Add code in "mainscene.cpp"

code show as below:

//监听选择场景的返回按钮
connect(chooseScene,&ChooseLevelScene::chooseSceneBack,[=](){
      this->show();
});

Run the project at this time, click the start button to switch to the level selection scene, and click the back button to return to the main scene.

4. Create a select level button

Now we need to implement the buttons for the 20 levels in the picture below. Add the code directly to "chooselevelscene.cpp". The code is still added in the constructor.

code show as below:

//创建关卡按钮
    for(int i = 0 ; i < 20;i++)
    {
        //运用自定义控件来生成关卡按钮
        MyPushButton * menuBtn = new MyPushButton(":/res/LevelIcon.png");
        menuBtn->setParent(this);
        //设置每个关卡按钮的位置  根据个人喜好还有前面设置的场景大小来设置
        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));//显示关卡数字
        label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); //设置居中
        label->move(40 + (i%4)*70 , 135+ (i/4)*70);//让数字在按钮上显示
        label->setAttribute(Qt::WA_TransparentForMouseEvents,true);  //鼠标事件穿透
    }

5. Realize the function of clicking the level button to jump to the corresponding gold coin flipping scene.

Like the previous scene creation, we need to redefine a class - PlayScene, to create a gold coin scene. There are 20 levels in total, but there is no need to create 20 classes, only one is needed. Different levels can be implemented using code. .

Right-click the project and select "Add New" --> select "C++" --> select "C++ Class" --> Next step --> set the class name "PlayScene" --> select the parent class "QMainWindow" --> Next step --> Complete

The successfully created project structure is as follows:

Because we need to click the corresponding button in the level selection scene and jump to the corresponding game interface, we can add the member variable pScene to the level selection scene "chooselevelscene.h" to point to the game interface corresponding to different levels.

Then use signals and slots in the level selection scene "chooselevelscene.cpp" to monitor the selected level and make the scene pointer defined above point to the corresponding scene. At this time, new PlayScene(i+1) will report an error because we have not overridden the constructor that creates the scene (implemented in the next part).


6. Realization of gold coin flipping scene

1. Set the basic configuration of the game scene

To create the corresponding level scene, you need to accept the level number passed from the level selection interface, so we define a variable in "playscene.h" to receive the level number, and declare the overloaded constructor at the same time

Then rewrite the constructor in "playscene.cpp" to implement the construction of the game scene, such as window size, menu bar and other basic functions.

code show as below:

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();});
}

2. Set the background image of the game scene

As before, you need to rewrite the paintEvent function, first declare it in "playscene.h", and then rewrite it in "playscene.cpp"

code show as below:

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);
}

Run the project at this time and click on any level to enter. The game scene is as follows:

3. Set the return button and its function

Just like the level selection scene, we need to set the special effects of pressing it, and also customize the return signal to the level selection scene for capture. Once captured, it will return to the level selection scene.

First declare the custom signal in "playscene.h", then create the return button and listen for the return button in "playscene.cpp"

code show as below:

//返回按钮
    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();
         }
        );
      });

Then set the return signal for listening to palyscene in the level selection scene, and add code in "chooselevelscene.cpp"

code show as below:

//PlayScene的返回按钮监听,删除该scene并将指针置为空
connect(pScene,&PlayScene::chooseSceneBack,[=](){
      this->setGeometry(pScene->geometry());
      this->show();
      delete pScene;
      pScene = NULL;
});

4. Display the current level number in the lower left corner

Add code in "palyscene.cpp" to display the level number

code show as below:

 //当前关卡标题
    QLabel * label = new QLabel;
    label->setParent(this);
    QFont font;
    font.setFamily("华文新魏");//设置字体
    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)); //设置大小和位置

Run the program at this time, select a level at will and you will see the level number in the lower left corner. The results are as follows:

5. Create a gold coin background in the middle of the game interface

Add code in "playscene.cpp"

code show as below:

//创建金币的背景图片
    for(int i = 0 ; i < 4;i++)
    {
        for(int j = 0 ; j < 4; j++)
        {
           //绘制背景图片
            QLabel* label = new QLabel;
            label->setGeometry(0,0,50,50);//设置大小
            label->setPixmap(QPixmap(":/res/BoardNode(1).png"));
            label->setParent(this);
            label->move(70 + i*50,200+j*50);//所在位置,对应x,y
        }
    }

At this time, run the project and select any level. The game interface is as follows:

6. Create gold coin category

After we create the gold coin background, we need to add the gold coins to the corresponding position. The gold coin needs to be clickable and have a flip effect after clicking, so we can separately encapsulate it into a gold coin class for easy use and creation.

 Like the previous scene creation, we need to redefine a class-MyCoin

Right-click the project and select "Add New" --> select "C++" --> select "C++ Class" --> Next step --> set the class name "MyCoin" --> select the parent class "QWidget" --> Next step --> Complete

After creation, the project structure is as follows:

Then modify the parent class of MyCoin class to QPushButton

In the resource pictures, we can see that the effect of gold coin flipping is achieved by switching between multiple pictures. In the following eight pictures, the normally displayed pictures are gold coin Coin0001 or silver coin Coin0008. Therefore, when we create a gold coin object, we should provide a parameter, which represents whether the gold coin resource path or the silver coin resource path is passed in. We create different styles of patterns based on the path. So we have to rewrite the constructor of gold coins.

First declare the overloaded constructor in "mycoin.h", and then implement the overloaded constructor in "mycoin.cpp"

code show as below:

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()));

}

Then we go to the game scene to add gold coins

code show as below:

//金币对象
MyCoin * coin = new MyCoin(":/res/Coin0001.png");
coin->setParent(this);
coin->move(72 + i*50,204+j*50);

At this time, run the project and select any level. The game interface is as follows:

7. Introduce level data

The initialization interface of each level is different, so we need to reference an existing level file. The file records the clear arrangement of gold coins for each level, which is the value of the two-dimensional array. (The files are provided in the preface, which are the dataconfig.h and dataconfig.cpp files)

First, put the dataConfig.h and dataConfig.cpp files into the current project:

Right-click the project and select "Add existing file" --> select two files to add.

After successful addition, the project structure is as follows:

8. Initialize each level

First, we declare a member variable in playscene.h to record the two-dimensional array of the current level. Then initialize this two-dimensional array in playscene.cpp, remember to add the header file "dataconfig.h". You can choose where to add the code, you can put it in the front, or you can put it like me

code show as below:

 //初始化二维数组
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];
    }
}    

After successful initialization, we need to define the attributes posX, posY, and flag in the MyCoin class. These three attributes respectively represent the x coordinate, y coordinate, and the current positive and negative flags of the gold coin in the two-dimensional array. (The front and back signs are used to determine whether the picture is a gold coin or a silver coin)

Then further initialize the gold coins. The previous addition of gold coins is just to add gold coins to see if the gold coins are created successfully. Now it is time to initialize the level. There are gold coins and silver coins.

code show as below:

 //金币对象
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坐标
coin->posY = j; //记录y坐标
coin->flag =gameArray[i][j]; //记录正反标志

Run the project at this time and click on any level. You can see that the game interface has been initialized successfully. The results are as follows:

9. Realize the special effect of flipping gold coins

After the initialization of the level is completed, the effect of clicking on the gold coin to flip is realized. We implement this method in the MyCoin class. First declare it in "mycoin.h", then implement the flip method in "mycoin.cpp", and create a timer in the constructor

code show as below:

//初始化定时器
    timer1 = new QTimer(this);
    timer2 = new QTimer(this);

void MyCoin::changeFlag()
{
    if(this->flag) //如果是正面,执行下列代码
    {
        timer1->start(30);
        this->flag = false;
    }
    else //反面执行下列代码
    {
        timer2->start(30);
        this->flag = true;
    }
}

After setting it up, we need to implement the flipping effect of gold coins and silver coins in the constructor. Just add the code after the timer we just set.

code show as below:

//监听正面翻转的信号槽
        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) //如果大于最大值,重置最小值,并停止定时器
            {
                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) //如果小于最小值,重置最大值,并停止定时器
            {
                this->max = 8;
                timer2->stop();
            }
        });

Then we monitor the click effect of each button and flip the gold coins to see if the flip effect is successful (this is just a test, so only the clicked gold coins will be flipped)

code show as below:

 connect(coin,&MyCoin::clicked,[=](){
   //qDebug() << "点击的位置: x = " <<  coin->posX << " y = " << coin->posY ;
   coin->changeFlag();
   gameArray[i][j] = gameArray[i][j] == 0 ? 1 : 0; //数组内部记录的标志同步修改
});

Run the project at this time, select any level, click on any gold coin to view the special effects:

At this time, the special effect of flipping the gold coin has been implemented, but if you click quickly, the new animation will continue to start after the gold coin has not performed a complete action. We should prohibit clicking again during the animation of the gold coin, and after completing the animation, Enable clicks.

We can add a flag isAnimation to the MyCoin class to represent whether the flip animation is being done. And set the flag to true in the changFlag() function, and change it to false after completing the animation.

At the same time, we rewrite the button press event and determine that if the animation is being executed, then return it directly without executing subsequent code. First declare it in "mycoin.h" and then implement it in "mycoin.cpp"

code show as below:

void MyCoin::mousePressEvent(QMouseEvent *e)
{
    if(this->isAnimation )
    {
        return;
    }
    else
    {
        return QPushButton::mousePressEvent(e);
    }
}

10. Realize the function of flipping surrounding gold coins

When the user clicks on the gold coin to flip, we let the four gold coins above, below, left and right also flip over in a delayed manner, and the code is written under the monitoring gold coin click button. At this point we also need to record the content of each button, so we also put all the gold coin buttons into a two-dimensional array and declare it in playscene.h

Then set the delay to flip other surrounding gold coins, and add the code in "playscene.cpp"

code show as below:

//翻转周围硬币
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+1][coin->posY]== 0 ? 1 : 0;
      }
      //周围下侧硬币翻转条件
      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. Determine whether victory is achieved

We add the isWin flag to playscene.h to represent victory or not.

The default setting is true. As long as there is one silver coin, the value is changed to false and it is regarded as unsuccessful. The code is written to make a judgment after delaying the flipping of gold coins.

code show as below:

//判断是否胜利
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;
        }
     }
}

Then make a general judgment

12. Display victory picture

We create the victory picture in advance. If victory is triggered, just pop the picture down. The code to create the victory image can be added at the front of the page.

code show as below:

 //创建胜利图片
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());

Then this picture pops up after judging the victory

code show as below:

//如果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. Disable button after victory

After victory, the click state of all buttons should be disabled. You can add the flag isWin to each button. If isWin is true, the MousePressEvent can be returned directly.

Modify the code in MousePressEvent

Disable all gold coins after judging victory

code show as below:

 //将所有按钮的胜利标志改为true,即禁用
for(int i = 0 ; i < 4;i++)
{
    for(int j = 0 ; j < 4; j++)
    {
        coinBtn[i][j]->isWin = true;
    }
}

7. Add sound effects

Add the start sound effect in "mainscene.cpp"

QSound *startSound = new QSound(":/res/TapButtonSound.wav",this);

Add "multimedia" at the end and then rebuild it

Play sound effects

Add select level sound effects in "chooselevelscene.cpp"

//选择关卡按钮音效
QSound *chooseSound = new QSound(":/res/TapButtonSound.wav",this);

Add the return button sound effect to "chooselevelscene.cpp" and "playscene.cpp" respectively

//返回按钮音效
    QSound *backSound = new QSound(":/res/BackButtonSound.wav",this);

Add the sound effects of turning gold coins and the sound effects of victory in playscene.cpp

 //翻金币音效
    QSound *flipSound = new QSound(":/res/ConFlipSound.wav",this);
    //胜利按钮音效
    QSound *winSound = new QSound(":/res/LevelWinSound.wav",this);


8. Optimization projects

1. The entry and return of the three scenes are all at the same position.

After we move the scene, if we enter the next scene and find that the scene is still in the center, if we want to set the position of the scene, we need to add the code as shown below:

Add in MainScene.cpp:

Add in ChooseScene.cpp:

2. Clicking on other gold coins at the moment of victory in the game resulted in failure to win (too fast)

To solve this bug, we only need to disable all buttons at the moment when we click to flip, and then restore them after all the surrounding gold coins are flipped.


9. Packaging projects

Click "Computer" on the left --> select the project --> select "release" --> then wait for the build to be successful and you can run it again

Open the path where the project is located --> find the corresponding folder ending with Release --> click the "release" folder --> copy an exe file

Create a folder on the desktop --> Copy and paste the CoinFlip.exe you just copied into it

Find the command window at the beginning and open it --> enter cd file directory and go to the desktop folder directory --> enter

"windeployqt CoinFlip.exe" press Enter and wait for completion

This is done. At this time, there will be many more files on the desktop folder.

In this way, the executable program has many dependency options. If you need to share the program with others, you need to send the entire folder, which is very troublesome.

Therefore we package the entire folder into a single executable .exe file.

Install the Enigma virtual box tool (just keep going next during the installation process)

Enigma virtual box official link: https://enigmaprotector.com/en/downloads.html

Click Add-->Add Folder Recursive-->Select the folder-->OK

Click Files Options-->Compress Files-->OK-->click Process in the lower right corner to generate

Then open the folder. The generated CoinFlip_boxed.exe is the packaged program. If you want to send it to others, just send this file.

Guess you like

Origin blog.csdn.net/m0_59800431/article/details/132795125