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

    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM