zoukankan      html  css  js  c++  java
  • 用C++、Qt实现的小游戏2048

    窗口布局与游戏截图:

    实现思路:

           1.使用二维数组模拟整个游戏网格,并将二维数组中每个数的大小用作游戏中每个网格中的数据。

           2.将对游戏的数据及数据的操作(即玩家的操作对游戏数据的影响)作为一个类,游戏的界面作为一个类,前一个类作为后一个类的成员存在

    具体实现:

    第一个类:游戏数据与数据操作

    类的定义:

    class myGame{
    public:
        myGame();
        void InitGame();           // 初始化游戏,清除已有数据,并在4*4的地图中随机在一个空位产生数字2
        void CreateNextTwo();      // 随机一个空位,并将这个位置的数字置为2
    
        // 游戏操作,即将整个地图中的数字向上(下、左、右)移动,直到碰到地图边界或者被某个已存
        // 在数字的位置挡住,在两个含有相同数字的位置碰撞时,两个数字会叠加作为前面那个位置新的
        // 数字,而后一个位置被置为空
        void Up();
        void Down();
        void Left();
        void Right();
    
    public:
        int data[MODE][MODE];      // 游戏数据,即地图上每个位置存在的数字大小,如果某个位置为空(即没有数字),则用0表示
        bool isEmpty[MODE][MODE];  // 用于记录每个位置是否为空
        int cntEmpty;              // 记录此时游戏中空位置的个数
                                   // 用途:1.当空位置为0时,游戏失败
                                   //      2.用于产生随机数,来确定下一次出现数字2的位置
        int score;                 // 用于计分,计分规则:整个地图中所有数字之和
    };

    类中函数的具体实现:

    myGame::myGame()
    {
        InitGame();
        CreateNextTwo();
    }
    
    void myGame::InitGame()
    {
        score = 0;    // 将得分初始化为0
    
        // 初始化所有位置上的数字,使其全为0
        for (int i = 0; i < MODE; i++){
            for (int j = 0; j < MODE; j++){
                data[i][j] = 0;
            }
        }
    
        // 重置cntEmpty的大小
        cntEmpty = MODE * MODE;
    }
    
    
    void myGame::CreateNextTwo()
    {
        srand((unsigned)time(NULL));       // 初始化随机数生成器
        int area = rand() % cntEmpty;  // 产生下一个随机出现数字的位置
        int cnt = 0;
    
        // 遍历整个二位数组,将随机到的位置上的数字设为2,
        for (int i = 0; i < MODE; i++){
            for (int j = 0; j < MODE; j++){
                if (data[i][j] == 0){
                    cnt++;
                    if (area == cnt - 1){
                        data[i][j] = 2;
                        break;
                    }
                }
            }
            if (area == cnt - 1){
                break;
            }
        }
    
        // 地图空位减一
        cntEmpty--;
        score += 2;    // 总分加2
    }
    
    void myGame::Up()
    {
        for (int i = 0; i < MODE; i++)
        {
            for (int j = 0; j < MODE; j++)
            {
                int cnt = j;
    
                while (data[j][i] == 0)
                {
                    cnt++;
    
                    // 将正在处理的数的下面的数据向上移动
                    for (int k = j; k < MODE - 1; k++)
                    {
                        data[k][i] = data[k + 1][i];
                    }
    
                    // 将最下面的数置为0
                    data[MODE - 1][i] = 0;
    
                    if (cnt >= MODE - 1)
                    {
                        break;
                    }
                }
    
                if (j != 0 && data[j][i] == data[j - 1][i])
                {
                    data[j - 1][i] *= 2;
                    data[j][i] = 0;
    
                    cnt = j;
    
                    while (data[j][i] == 0)
                    {
                        cnt++;
    
                        // 将正在处理的数的下面的数据向上移动
                        for (int k = j; k < MODE - 1; k++)
                        {
                            data[k][i] = data[k + 1][i];
                        }
    
                        // 将最下面的数置为0
                        data[MODE - 1][i] = 0;
    
                        if (cnt >= MODE - 1)
                        {
                            break;
                        }
                    }
                }
            }
        }
    }
    
    void myGame::Down()
    {
        for (int i = 0;  i < MODE; i++)
        {
            for (int j = 0; j < MODE; j++)
            {
                int cnt = j;
    
                while (data[MODE - 1 - j][i] == 0)
                {
                    cnt++;
    
                    // 将此事正在处理的数的上面的数向下平移
                    for (int k = j; k < MODE - 1; k++)
                    {
                        data[MODE - 1 - k][i] = data[MODE - 1 - k - 1][i];
                    }
    
                    // 将最上面的数置为0
                    data[0][i] = 0;
    
                    if (cnt >= MODE - 1)
                    {
                        break;
                    }
                }
    
                if (j != 0 && data[MODE - 1 - j][i] == data[MODE -j][i])
                {
                    data[MODE - j][i] *= 2;
                    data[MODE - 1 - j][i] = 0;
    
                    cnt = j;
    
                    while (data[MODE - 1 - j][i] == 0)
                    {
                        cnt++;
    
                        // 将此事正在处理的数的上面的数向下平移
                        for (int k = j; k < MODE - 1; k++)
                        {
                            data[MODE - 1 - k][i] = data[MODE - 1 - k - 1][i];
                        }
    
                        // 将最上面的数置为0
                        data[0][i] = 0;
    
                        if (cnt >= MODE - 1)
                        {
                            break;
                        }
                    }
                }
            }
        }
    }
    
    void myGame::Left()
    {
        for (int i = 0; i < MODE; i++)
        {
            for (int j = 0; j < MODE; j++)
            {
                int cnt = j;    // 用于计数
    
                // 当此时处理的数为0时
                while (data[i][j] == 0)
                {
                    cnt++;
    
                    // 将这个数后面的数向前移动
                    for (int k = j; k < MODE - 1; k++)
                    {
                        data[i][k] = data[i][k + 1];
                    }
    
                    // 将最后面的数置为0
                    data[i][MODE - 1] = 0;
    
                    // 如果处理次数达到最大,退出循环,避免因为一组数据全部为0时导致无限循环
                    if (cnt >= MODE - 1)
                    {
                        break;
                    }
                }
    
                // 当此时处理的数据不是第一个数据,且其与在它前一位的数据相等时
                if (j != 0 && data[i][j] == data[i][j - 1])
                {
                    // 依据游戏规则,将这两个数合并,并将合并后的值作为前一个数的值,并将后一个数的值置为0
                    data[i][j - 1] *= 2;
                    data[i][j] = 0;
    
                    cnt = j;
    
                    // 同上一步,处理为0的数据
                    while (data[i][j] == 0)
                    {
                        cnt++;
    
                        // 将这个数后面的数向前移动
                        for (int k = j; k < MODE - 1; k++)
                        {
                            data[i][k] = data[i][k + 1];
                        }
    
                        // 将最后面的数置为0
                        data[i][MODE - 1] = 0;
    
                        // 如果处理次数达到最大,退出循环,避免因为一组数据全部为0时导致无限循环
                        if (cnt >= MODE - 1)
                        {
                            break;
                        }
                    }
                }
            }
        }
    }
    
    void myGame::Right()
    {
        for (int i = 0; i < MODE; i++)
        {
            for (int j = 0; j < MODE; j++)
            {
                int cnt = j;
    
                while (data[i][MODE - 1 - j] == 0)
                {
                    cnt++;
    
                    // 将左边的数据向右移动
                    for (int k = j; k < MODE - 1; k++)
                    {
                        data[i][MODE - 1 - k] = data[i][MODE - 1 - k - 1];
                    }
    
                    // 将最左边的数据置为0
                    data[i][0] = 0;
    
                    if (cnt >= MODE - 1)
                    {
                        break;
                    }
                }
    
                if (j != 0 && data[i][MODE - 1 - j] == data[i][MODE - j ])
                {
                    data[i][MODE - j] *= 2;
                    data[i][MODE - 1 - j] = 0;
    
                    int cnt = j;
    
                    while (data[i][MODE - 1 - j] == 0)
                    {
                        cnt++;
    
                        // 将左边的数据向右移动
                        for (int k = j; k < MODE - 1; k++)
                        {
                            data[i][MODE - 1 - k] = data[i][MODE - 1 - k - 1];
                        }
    
                        // 将最左边的数据置为0
                        data[i][0] = 0;
    
                        if (cnt >= MODE - 1)
                        {
                            break;
                        }
                    }
                }
            }
        }
    }

    第二个类:游戏界面与游戏中的操作

    类的定义:

    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
    
        void ShowGame();
    
    private slots:
        void on_pushButton_up_clicked();
    
        void on_pushButton_down_clicked();
    
        void on_pushButton_left_clicked();
    
        void on_pushButton_right_clicked();
    
        void on_actionReset_triggered();
    
        void on_actionQuit_triggered();
    
    private:
        Ui::MainWindow *ui;
        QTextBrowser *textBrower[MODE][MODE];
    
        myGame game;
    };

    类的构造函数:

    整个界面使用Qt Designer画的

    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        // 关联
        textBrower[0][0] = ui->textBrowser1;
        textBrower[0][1] = ui->textBrowser_2;
        textBrower[0][2] = ui->textBrowser_3;
        textBrower[0][3] = ui->textBrowser_4;
        textBrower[1][0] = ui->textBrowser_5;
        textBrower[1][1] = ui->textBrowser_6;
        textBrower[1][2] = ui->textBrowser_7;
        textBrower[1][3] = ui->textBrowser_8;
        textBrower[2][0] = ui->textBrowser_9;
        textBrower[2][1] = ui->textBrowser_10;
        textBrower[2][2] = ui->textBrowser_11;
        textBrower[2][3] = ui->textBrowser_12;
        textBrower[3][0] = ui->textBrowser_13;
        textBrower[3][1] = ui->textBrowser_14;
        textBrower[3][2] = ui->textBrowser_15;
        textBrower[3][3] = ui->textBrowser_16;
    
        ShowGame();
    }

    信号、槽(对用户操作的反应):

    void MainWindow::ShowGame()   // 将整个游戏数据显示到游戏界面
    {
        for (int i = 0; i < MODE; i++)
        {
            for (int j = 0; j < MODE; j++)
            {
                if (game.data[i][j] != 0)    // 当不为0时显示数字
                {
                    textBrower[i][j]->setText(QString::number(game.data[i][j]));
                }
                else                         // 当为0时不显示
                {
                    textBrower[i][j]->setText(" ");
                }
            }
        }
    
        ui->lineEdit->setText(QString::number(game.score));
        ui->lineEdit_2->setText(QString::number(MODE));
    }
    
    // 各个操作
    void MainWindow::on_pushButton_up_clicked()
    {
        game.Up();
        game.CreateNextTwo();
        ShowGame();
    }
    
    void MainWindow::on_pushButton_down_clicked()
    {
        game.Down();
        game.CreateNextTwo();
        ShowGame();
    }
    
    void MainWindow::on_pushButton_left_clicked()
    {
        game.Left();
        game.CreateNextTwo();
        ShowGame();
    }
    
    void MainWindow::on_pushButton_right_clicked()
    {
        game.Right();
        game.CreateNextTwo();
        ShowGame();
    }
    
    void MainWindow::on_actionReset_triggered()
    {
        game.InitGame();
        game.CreateNextTwo();
        ShowGame();
    }
    
    void MainWindow::on_actionQuit_triggered()
    {
        close();
    }

    总结:

    在整个的实现过程中,卡壳的地方主要是对整个数据的上下左右的操作的实现。总是想着一下子将整个操作步骤写完,认为自己

    想的实现方法在逻辑上没有问题,那么实现之后也就不会出问题。然而总会发现有着自己没有考虑到的地方,并且程导致序运行

    后出现的错误不止如何改正。后来学乖了,老老实实的将整个步骤一步步拆开,一步步的实现。

    在写将二维数组的整个数据向某个方向移动时(上述类 myGame中写的游戏操作),虽然移动的方向不同,但整体逻辑是一样的,

    并且在每一的方向上的移动,都可以将二维数组拆分为MODE个一维数组,而对于每个一维数组的操作都是相同的,可以先实现

    对一维数组的操作,然后在推广到二维数组。代码如下:

    void OnePart(int *data)
    {
        for (int i = 0; i < MODE; i++){
            int cnt = i;
    
            // 为0的情况
    
            while (data[i] == 0){
                cnt++;
    
                for (int j = i; j < MODE - 1; j++){
                    data[j] = data[j + 1];
                }
    
                data[MODE - 1] = 0;
    
                if (cnt >= MODE - 1){
                    break;
                }
            }
    
            // 不为0且此时处理的数不是这一组第一个被处理的数的情况
    
            if (i != 0 && data[i] == data[i - 1]){
                data[i - 1] = 2 * data[i - 1];
                data[i] = 0;
    
                cnt = i;
    
                while (data[i] == 0){
                    cnt++;
    
                    for (int k = i; k < 5 - 1; k++){
                        data[k] = data[k + 1];
                    }
    
                    data[MODE - 1] = 0;
    
                    if (cnt >= MODE - 1){
                        break;
                    }
                }
            }
    
        }
    }
  • 相关阅读:
    subprocess 的 Popen用法
    subprocess之check_out用法
    Appium使用总结
    如何将pyqt5的qt-designer设计出来的 .ui 和 .qrc 文件转化成 .py 文件
    python serial模块使用,是pyserial而非serial
    基于 Tensorflow 实现 Mobilenet V1 并基于 CFAR-10 数据训练
    预测单词词性
    单词纠错系统
    Python深度学习 deep learning with Python
    书单
  • 原文地址:https://www.cnblogs.com/lnlin/p/8668660.html
Copyright © 2011-2022 走看看