1. 程式人生 > >Qt淺談之二十五2048遊戲(原始程式碼來自網路)

Qt淺談之二十五2048遊戲(原始程式碼來自網路)

一、簡介

        Qt改寫2048遊戲,在linux系統下找尋android的視覺效果。

二、執行圖

        啟動執行圖:

三、詳解

1、程式碼

(1)widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QMessageBox>
#include <QFile>
#include <QtGui>
#include "GameWidget.h"

// 主視窗部件類
class Widget : public QWidget
{
    Q_OBJECT

public:
    // 構造&解構函式
    Widget(QWidget *parent = 0);
    ~Widget();

private:
    // “restart”按鈕
    QPushButton *restartBtn;
    QPushButton *closeBtn;
    // “score”標籤
    QLabel *titleLabel;
    QLabel *tipsLabel;
    QLabel *scoreLbl;
    // “HightScore”標籤
    QLabel *highScoreLbl;
    // 遊戲部件
    GameWidget *gameWidget;
    // 寬度和高度的縮放比例 用來使視窗部件隨主視窗的尺寸改變而改變位置與尺寸
    qreal ratioW, ratioH;
    // 記錄最高分
    int highScore;
    QPoint dragPosition;
    bool bPressFlag;

protected:
    // 視窗尺寸改變觸發的事件
    void resizeEvent(QResizeEvent *);
    void keyPressEvent(QKeyEvent * event);
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);

public slots:
    // 處理分數增加訊號的槽函式
    void onScoreInc(int);
    // 處理遊戲結束訊號的槽函式
    void onGameOver();
    // 處理遊戲獲勝訊號的槽函式
    void onWin();
};

#endif // WIDGET_H
(2)widget.cpp
#include "widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent, Qt::FramelessWindowHint)
    , bPressFlag(false)
{
    setAutoFillBackground(true);
    QPalette pal = palette();
    pal.setColor(QPalette::Background, QColor("#FAF8F0"));
    setPalette(pal);

    titleLabel = new QLabel(tr("2048"), this);
    titleLabel->setStyleSheet("color:#746D65;");
    titleLabel->setFont(QFont("arial", 45, QFont::Black));

    tipsLabel = new QLabel(tr("Join the numbers and get to the 2048 tile!"), this);
    tipsLabel->setStyleSheet("color:#B3AFA7;");
    tipsLabel->setFont(QFont("arial", 10, QFont::Normal));
    // 初始化最高分為0
    highScore = 0;
    // 讀取儲存最高分的檔案
    QFile file("score.j");
    if (file.open(QIODevice::ReadOnly))
    {
        file.read((char *)&highScore, sizeof(highScore));
        file.close();
    }

    // 構造一個遊戲部件物件
    gameWidget = new GameWidget(this);
    //gameWidget->setGeometry(2, 200, 400, 400);
    gameWidget->setFocus();
    connect(gameWidget, SIGNAL(ScoreInc(int)), this, SLOT(onScoreInc(int)));
    connect(gameWidget, SIGNAL(GameOver()), this, SLOT(onGameOver()));
    connect(gameWidget, SIGNAL(win()), this, SLOT(onWin()));

    // 構造一個字型物件
    QFont font;
    font.setFamily("Arial");
    font.setBold(true);
    font.setPixelSize(15);

    // 構造一個按鈕物件
    restartBtn = new QPushButton("New Game", this);
    //restartBtn->setGeometry(100, 120, 200, 50);
    restartBtn->setFont(font);
    restartBtn->setStyleSheet("QPushButton{color: white;background:#907963;border-style:flat;}");
    //restartBtn->setFlat(true);
    restartBtn->setFocusPolicy(Qt::NoFocus);
    connect(restartBtn, SIGNAL(clicked()), gameWidget, SLOT(restart()));

    // 構造一個標籤物件
    highScoreLbl = new QLabel(QString("BEST\n%1").arg(highScore),this);
    //highScoreLbl->setGeometry(220, 30, 150, 50);
    highScoreLbl->setFont(font);
    highScoreLbl->setAlignment(Qt::AlignCenter);
    highScoreLbl->setStyleSheet("QLabel{color:#FFFFEE;background:#BEAD9D}");

    // 構造一個標籤物件
    scoreLbl = new QLabel("SCORE\n0", this);
    //scoreLbl->setGeometry(30, 30, 150, 50);
    scoreLbl->setFont(font);
    scoreLbl->setAlignment(Qt::AlignCenter);
    scoreLbl->setStyleSheet("QLabel{color:#FFFFEE;background:#BEAD9D}");

    closeBtn = new QPushButton("x", this);
    closeBtn->setFocusPolicy(Qt::NoFocus);
    closeBtn->setFont(QFont("Arial", 11, QFont::Normal));

    closeBtn->setStyleSheet("QPushButton{border-style:flat;color:#BDAD9F}"
                        "QPushButton:hover{border-style:flat;color:#FF0000}");
    closeBtn->setCursor(Qt::PointingHandCursor);
    closeBtn->setGeometry(400 - 18, 3, 15, 15);
    connect(closeBtn, SIGNAL(clicked()), this, SLOT(close()));

    // 重置視窗大小
    resize(400, 510);
    move((QApplication::desktop()->width() - width())/2,  (QApplication::desktop()->height() - height())/2);
}

Widget::~Widget()
{
    // 釋放相關物件
    delete restartBtn;
    delete scoreLbl;
    delete highScoreLbl;
    delete gameWidget;
}

void Widget::onScoreInc(int score)
{
    // 更新分數顯示
    scoreLbl->setText(QString("Score:\n %1").arg(score));
    // 如果當前分數高於最高分
    if (score > highScore)
    {
        // 更新最高分
        highScore = score;
        highScoreLbl->setText(QString("BEST:\n %1").arg(highScore));

        // 將新的最高分存入檔案
        QFile file("score.j");
        file.open(QIODevice::WriteOnly);
        file.write((char *)&highScore, sizeof(highScore));
        file.close();
    }
}

void Widget::onGameOver()
{
    QMessageBox::information(this, "GameOver", "You lost !");
}

void Widget::onWin()
{
    QMessageBox::information(this, "Congratulation", "You win !");
}

void Widget::resizeEvent(QResizeEvent *)
{
    // 計算寬度和高度的縮放比例
    ratioW = width() / 400.0f;
    ratioH = height() / 510.0f;
    // 重置子部件大小和位置
    titleLabel->setGeometry(20 * ratioW, 40 * ratioH , 130 * ratioW, 50 * ratioH);
    tipsLabel->setGeometry(20 * ratioW, 100 * ratioH , 300 * ratioW, 20 * ratioH);
    gameWidget->setGeometry(18 * ratioW, 140 * ratioH, 365 * ratioW, 365 * ratioH);
    restartBtn->setGeometry(280 * ratioW, 90 * ratioH, 100 * ratioW, 30 * ratioH);
    highScoreLbl->setGeometry(300 * ratioW, 40 * ratioH, 80 * ratioW, 40 * ratioH);
    scoreLbl->setGeometry(210 * ratioW, 40 * ratioH, 80 * ratioW, 40 * ratioH);
}

void Widget::keyPressEvent(QKeyEvent *event)
{
    gameWidget->setFocus();
    QWidget::keyPressEvent(event);
}
void Widget::mousePressEvent ( QMouseEvent * event)
{
    bPressFlag = true;
    dragPosition = event->pos();
    QWidget::mousePressEvent(event);
}

void Widget::mouseMoveEvent(QMouseEvent *event)
{
    if (bPressFlag) {
        QPoint relaPos(QCursor::pos() - dragPosition);
        move(relaPos);
    }
    QWidget::mouseMoveEvent(event);
}

void Widget::mouseReleaseEvent(QMouseEvent *event)
{
    bPressFlag = false;
    QWidget::mouseReleaseEvent(event);
}
(3)GameWidget.h
#ifndef GAMEWIDGET_H
#define GAMEWIDGET_H

#include <QWidget>
#include <QMouseEvent>
#include <QEventLoop>
#include <QTimer>
#include <QPainter>
#include <QList>

// 手勢的方向
enum GestureDirect
{
    LEFT = 0,   // 向左
    RIGHT = 1,  // 向右
    UP = 2,     // 向上
    DOWN = 3    // 向下
};

// 定義動畫的型別
enum AnimationType
{
    MOVE = 0,       // 方格移動動畫
    APPEARANCE = 1  // 方格出現動畫
};

// 動畫結構體
struct Animation
{
    AnimationType type;     // 動畫型別
    GestureDirect direct;   // 方向
    QPointF startPos;       // 起始點座標 出現動畫僅僅使用這個座標
    QPointF endPos;         // 終止點座標 移動動畫的終點座標
    int digit;              // 數碼
    int digit2;             // 第二數碼 數碼可能被合併
};

// 遊戲部件類 繼承自QWidget
class GameWidget : public QWidget
{
    Q_OBJECT
public:
    // 建構函式
    explicit GameWidget(QWidget *parent = 0);

protected:
    void keyPressEvent(QKeyEvent * event);

private:
    // 遊戲面板 儲存每個格子的數值
    int board[4][4];
    // 數碼的個數 儲存當前面板上的數字的個數
    int digitCount;
    // 分數 儲存當前得分
    int score;
    // 起始點座標 儲存手勢起點座標
    QPoint startPos;
    // 儲存所有需要展現的動畫
    QList<Animation> animationList;
    // 小格子的寬度和高度
    qreal w, h;
    // 快取影象
    QImage *cacheImg;
    // 是否在播放動畫效果
    bool isAnimating;

    // 檢測遊戲是否結束
    bool checkGameOver();
    // 檢測遊戲是否獲勝
    bool checkWin();
    /* 獲取一個數字的二進位制位數 當然這裡獲取的不完全是二進位制位數 而是對應顏色陣列的下標
    比如 2 對應 0    8 對應 2*/
    int getBitCount(int);
    // 繪製動畫效果
    bool playAnimation(Animation&, QPainter&);
    // 滑鼠按下觸發的事件
    void mousePressEvent(QMouseEvent *);
    // 滑鼠釋放觸發的時間
    void mouseReleaseEvent(QMouseEvent *);
    // 繪製事件
    void paintEvent(QPaintEvent *);

    // 以下為一些訊號
signals:
    // 手勢移動訊號
    void GestureMove(GestureDirect);
    // 分數增加訊號
    void ScoreInc(int);
    // 遊戲結束訊號
    void GameOver();
    // 遊戲獲勝訊號
    void win();

    // 以下為一些槽函式
public slots:
    // 處理手勢移動訊號的槽函式
    void onGestureMove(GestureDirect);
    // 重新開始的槽函式
    void restart();

};

#endif // GAMEWIDGET_H
(4)GameWidget.cpp
#include <QDebug>
#include "GameWidget.h"

// 顏色陣列 儲存每個數字對應的背景色
QColor digitBkg[11] = {QColor::fromRgb(0xEE, 0xE5, 0xDB), QColor::fromRgb(0xEC, 0xE0, 0xC8),
                            QColor::fromRgb(0xF2, 0xAF, 0x78), QColor::fromRgb(0xEE, 0x8A, 0x54),
                            QColor::fromRgb(0xFE, 0x76, 0x5E), QColor::fromRgb(0xE7, 0x58, 0xC),
                            QColor::fromRgb(0xFF, 0x66, 0x66), QColor::fromRgb(0xF1, 0xCF, 0x48),
                            QColor::fromRgb(0xCC, 0x33, 0x33), QColor::fromRgb(0xE3, 0xB9, 0x11),
                            QColor::fromRgb(0xFF, 0x00, 0x00)};

// 每個方向位置的增量
QPointF dPos[5] = {QPointF(-10, 0), QPointF(10, 0), QPointF(0, -10), QPointF(0, 10), QPointF(-2, -2)};

GameWidget::GameWidget(QWidget *parent) :
    QWidget(parent)
{
    // 連線手勢移動訊號和相應的槽函式
    connect(this, SIGNAL(GestureMove(GestureDirect)), SLOT(onGestureMove(GestureDirect)));
    // 初始化board陣列
    memset(board, 0, sizeof(board));
    // 隨機填入兩個2
    board[rand() % 4][rand() % 4] = 2;
    while(true) {
        int x = rand() % 4;
        int y = rand() % 4;
        if (board[x][y] != 0) {
            continue;
        }
        else {
            board[x][y] = 2;
            break;
        }
    }
    // 分數初始化為0
    score = 0;
    // 數碼個數初始化為2
    digitCount = 2;
    isAnimating = false;
    cacheImg = NULL;
}

void GameWidget::keyPressEvent(QKeyEvent *event)
{
    if (isAnimating)
        return;
    switch (event->key()) {
        case Qt::Key_Left:
            emit GestureMove(LEFT);
        break;
        case Qt::Key_Right:
            emit GestureMove(RIGHT);
        break;
        case Qt::Key_Down:
            emit GestureMove(DOWN);
        break;
        case Qt::Key_Up:
            emit GestureMove(UP);
        break;
        default:
        break;
    }
    QWidget::keyPressEvent(event);
}

void GameWidget::mousePressEvent(QMouseEvent *e)
{
    // 獲取起點座標
    startPos = e->pos();
}

void GameWidget::mouseReleaseEvent(QMouseEvent *e)
{
    // 如果在播放動畫效果則直接退出防止重複產生手勢事件
    if (isAnimating)
        return;
    // 根據終點座標和起點座標計算XY座標的增量
    float dX = (float)(e->pos().x() - startPos.x());
    float dY = (float)(e->pos().y() - startPos.y());
    // 確定手勢方向
    if (abs(dX) > abs(dY))
    {
        if (dX < 0)
            emit GestureMove(LEFT);
        else
            emit GestureMove(RIGHT);
    }
    else
    {
        if (dY < 0)
            emit GestureMove(UP);
        else
            emit GestureMove(DOWN);
    }
}

void GameWidget::onGestureMove(GestureDirect direct)
{
    int i, j, k;
    Animation a;
    // 是否合併過方格
    bool combine = false;
    // 處理不同方向
    switch (direct)
    {
    // 向左
    case LEFT:
        // 迴圈每一行
        for (i = 0; i < 4; i++)
        {
            /* 初始化j k為0
             * 這裡j表示要交換的數字列號
             * k表示交換到的位置的列號
             * */
            j = 0, k = 0, combine = false;
            while (true)
            {
                // 迴圈找到第一個不是0的數字對應的列號
                while (j < 4 && board[i][j] == 0)
                    j++;
                // 如果超過了3則說明搜尋完畢 推出迴圈
                if (j > 3)
                    break;

                // 交換兩個數字
                qSwap(board[i][k], board[i][j]);
                // 記錄動畫資訊
                a.type = MOVE;
                a.startPos = QPointF(7 + (w + 5) * j, 7 + (h + 5) * i);
                a.endPos = QPointF(7 + (w + 5) * k, 7 + (h + 5) * i);
                a.digit = a.digit2 = board[i][k];
                a.direct = LEFT;
                //如果交換後的數字與其前一列的數字相同
                if (!combine && k > 0 && board[i][k] == board[i][k - 1])
                {
                    // 前一列的數字*2
                    board[i][k - 1] <<= 1;
                    // 這一列的數字置為0
                    board[i][k] = 0;
                    // 記錄動畫資訊
                    a.digit2 = board[i][k - 1];
                    a.endPos = QPointF(7 + (w + 5) * (k - 1), 7 + (h + 5) * i);
                    // 增加分數
                    score += board[i][k - 1];
                    // 發射增加分數的訊號
                    emit ScoreInc(score);
                    // 數碼個數-1
                    digitCount--;
                    combine = true;
                }
                else
                    k++;
                j++;
                // 新增到動畫連結串列
                animationList.append(a);
            }
        }
        break;
        // 其餘三個方向與左向類似
    case RIGHT:
        for (i = 0; i < 4; i++)
        {
            j = 3, k = 3, combine = false;
            while (true)
            {
                while (j > -1 && board[i][j] == 0)
                    j--;
                if (j < 0)
                    break;
                qSwap(board[i][k], board[i][j]);
                a.type = MOVE;
                a.startPos = QPointF(7 + (w + 5) * j, 7 + (h + 5) * i);
                a.endPos = QPointF(7 + (w + 5) * k, 7 + (h + 5) * i);
                a.digit = a.digit2 = board[i][k];
                a.direct = RIGHT;
                if (!combine && k < 3 && board[i][k] == board[i][k + 1])
                {
                    board[i][k + 1] <<= 1;
                    board[i][k] = 0;
                    a.digit2 = board[i][k + 1];
                    a.endPos = QPointF(7 + (w + 5) * (k + 1), 7 + (h + 5) * i);
                    score += board[i][k + 1];
                    emit ScoreInc(score);
                    digitCount--;
                    combine = true;
                }
                else
                    k--;
                j--;
                animationList.append(a);
            }
        }
        break;
    case UP:
        for (i = 0; i < 4; i++)
        {
            j = 0, k = 0, combine = false;
            while (true)
            {
                while (j < 4 && board[j][i] == 0)
                    j++;
                if (j > 3)
                    break;
                qSwap(board[k][i], board[j][i]);
                a.type = MOVE;
                a.startPos = QPointF(7 + (w + 5) * i, 7 + (h + 5) * j);
                a.endPos = QPointF(7 + (w + 5) * i, 7 + (h + 5) * k);
                a.digit = a.digit2 = board[k][i];
                a.direct = UP;
                if (!combine && k > 0 && board[k][i] == board[k - 1][i])
                {
                    board[k - 1][i] <<= 1;
                    board[k][i] = 0;
                    a.digit2 = board[k - 1][i];
                    a.endPos = QPointF(7 + (w + 5) * i, 7 + (h + 5) * (k - 1));
                    score += board[k - 1][i];
                    emit ScoreInc(score);
                    digitCount--;
                    combine = true;
                }
                else
                    k++;
                j++;
                animationList.append(a);
            }
        }
        break;
    case DOWN:
        for (i = 0; i < 4; i++)
        {
            j = 3, k = 3, combine = false;
            while (true)
            {
                while (j > -1 && board[j][i] == 0)
                    j--;
                if (j < 0)
                    break;
                qSwap(board[k][i], board[j][i]);
                a.type = MOVE;
                a.startPos = QPointF(7 + (w + 5) * i, 7 + (h + 5) * j);
                a.endPos = QPointF(7 + (w + 5) * i, 7 + (h + 5) * k);
                a.digit = a.digit2 = board[k][i];
                a.direct = DOWN;
                if (!combine && k < 3 && board[k][i] == board[k + 1][i])
                {
                    board[k + 1][i] <<= 1;
                    board[k][i] = 0;
                    a.digit2 = board[k + 1][i];
                    a.endPos = QPointF(7 + (w + 5) * i, 7 + (h + 5) * (k + 1));
                    score += board[k + 1][i];
                    emit ScoreInc(score);
                    digitCount--;
                    combine = true;
                }
                else
                    k--;
                j--;
                animationList.append(a);
            }
        }
        break;
    }
    bool flag_move = false;
    for(int index = 0; index < animationList.size(); index++) {
        if (animationList.at(index).startPos != animationList.at(index).endPos) {
            flag_move = true;
            break;
        }
    }
    // 如果數字木有填滿
    if (digitCount != 16 && flag_move)
    {
        // 隨機產生行號和列號
        i = rand() % 4, j = rand() % 4;
        // 迴圈直到行和列對應的元素為0
        while (board[i][j] != 0)
            i = rand() % 4, j = rand() % 4;
        // 填入2
        //board[i][j] = (rand() % 2 + 1) * 2;
        board[i][j] = 2;
        // 記錄動畫資訊
        a.type = APPEARANCE;
        a.startPos = a.endPos = QPointF(7 + (w + 5) * j, 7 + (h + 5) * i);
        a.startPos += QPointF(w / 2, h / 2);
        a.digit = board[i][j];
        // 數碼個數加一
        digitCount++;
    }
    else
    {
        // 如果數字填滿了 檢測遊戲是否over
        if (checkGameOver())
            emit GameOver();// 如果遊戲over了則發射GameOver訊號
    }

    // 開始繪製動畫效果
    isAnimating = true;
    // 動畫列表的迭代器
    QList<Animation>::iterator it;
    // 事件迴圈 用於延時
    QEventLoop eventLoop;
    // 刪除之前的快取影象
    if (cacheImg)
        delete cacheImg;
    // 建立快取影象
    cacheImg = new QImage(width(), height(), QImage::Format_ARGB32);
    // 清空影象
    cacheImg->fill(0);
    // 構造一個QPainter物件
    QPainter painter(cacheImg);
    // 字型
    QFont font;
    font.setFamily("Consolas");
    font.setBold(true);
    font.setPixelSize(40);
    painter.setFont(font);
    // 標識所有方格動畫是否都播放完畢
    bool ok = false;
    while (true)
    {
        // 構造一個畫刷 顏色為R G B分量分別為141 121 81的顏色
        QBrush brush(QColor::fromRgb(141, 121, 81));
        // 使painter應用這個畫刷
        painter.setBrush(brush);

        // 設定畫筆為空筆 目的是使繪製的圖形沒有描邊
        painter.setPen(Qt::NoPen);

        // 繪製一個矩形
        painter.drawRect(2, 2, width() - 4, height() - 4);

        // 設定畫刷顏色為 RGB分量為171 165 141的顏色
        brush.setColor(QColor::fromRgb(171, 165, 141));
        // 應用這個畫刷
        painter.setBrush(brush);

        // 迴圈繪製遊戲面板
        for (int i = 0; i < 4; i++)
            for (int j = 0; j < 4; j++)
                // 繪製小方格
                painter.drawRect(QRectF(7 + (w + 5) * j, 7 + (h + 5) * i, w, h));

        // 假設都播放完畢
        ok = true;

        // 迴圈播放每個方格動畫
        for (it = animationList.begin(); it != animationList.end(); it++)
            if (!playAnimation(*it, painter))
                ok = false;

        // 重新整理部件
        update();

        // 全部播放完則退出
        if (ok)
            break;

        // 延時5ms
        QTimer::singleShot(5, &eventLoop, SLOT(quit()));
        eventLoop.exec();
    }
    // 播放方格出現的動畫
    while (!playAnimation(a, painter))
    {
        update();
        QTimer::singleShot(5, &eventLoop, SLOT(quit()));
        eventLoop.exec();
    }
    //清除所有動畫
    animationList.clear();
    //重新整理當前部件
    isAnimating = false;

    // 檢測遊戲是否獲勝
    if (checkWin())
        emit win();// 如果獲勝則發射win訊號

    update();
}

bool GameWidget::playAnimation(Animation& a, QPainter& painter)
{
    bool rtn = false;
    QBrush brush(Qt::SolidPattern);

    // 移動方格位置
    if (a.type == MOVE)
    {
        switch (a.direct)
        {
        case LEFT:
            if (a.startPos.x() > a.endPos.x())
                a.startPos += dPos[LEFT];
            else
                a.startPos = a.endPos, rtn = true;
            break;
        case RIGHT:
            if (a.startPos.x() < a.endPos.x())
                a.startPos += dPos[RIGHT];
            else
                a.startPos = a.endPos, rtn = true;
            break;
        case UP:
            if (a.startPos.y() > a.endPos.y())
                a.startPos += dPos[UP];
            else
                a.startPos = a.endPos, rtn = true;
            break;
        case DOWN:
            if (a.startPos.y() < a.endPos.y())
                a.startPos += dPos[DOWN];
            else
                a.startPos = a.endPos, rtn = true;
        }
        // 如果方格移動到終點
        if (!rtn)
        {
            brush.setColor(digitBkg[getBitCount(a.digit)]);
            painter.setBrush(brush);
            painter.drawRect(QRectF(a.startPos.x(), a.startPos.y(), w, h));
            painter.setPen(QColor::fromRgb(0, 0, 0));
            painter.drawText(QRectF(a.startPos.x(), a.startPos.y(), w, h), Qt::AlignCenter,
                             QString::number(a.digit));
        }
        else
        {
            brush.setColor(digitBkg[getBitCount(a.digit2)]);
            painter.setBrush(brush);
            painter.drawRect(QRectF(a.startPos.x(), a.startPos.y(), w, h));
            painter.setPen(QColor::fromRgb(0, 0, 0));
            painter.drawText(QRectF(a.startPos.x(), a.startPos.y(), w, h), Qt::AlignCenter,
                             QString::number(a.digit2));
        }
        painter.setPen(Qt::NoPen);
    }
    else
    {
        // 方格出現的動畫效果
        if (a.startPos.x() > a.endPos.x())
            a.startPos += dPos[4];
        else
            a.startPos = a.endPos, rtn = true;
        brush.setColor(digitBkg[getBitCount(a.digit)]);
        painter.setBrush(brush);
        painter.drawRect(QRectF(a.startPos.x(), a.startPos.y(),
                         w - 2 * (a.startPos.x() - a.endPos.x()),
                         h - 2 * (a.startPos.y() - a.endPos.y())));
        painter.setPen(QColor::fromRgb(0, 0, 0));
        painter.drawText(QRectF(a.endPos.x(), a.endPos.y(), w, h),
                         Qt::AlignCenter, QString::number(a.digit));
        painter.setPen(Qt::NoPen);
    }
    return rtn;
}

void GameWidget::paintEvent(QPaintEvent *)
{
    // 構造一個QPainter物件 使用它來進行繪圖
    QPainter painter(this);

    // 如果正在播放動畫效果則繪製快取點陣圖
    if (isAnimating)
    {
        painter.drawImage(0, 0, *cacheImg);
        return;
    }

    // 構造一個畫刷 顏色為R G B分量分別為141 121 81的顏色
    QBrush brush(QColor::fromRgb(141, 121, 81));
    // 使painter應用這個畫刷
    painter.setBrush(brush);

    // 設定畫筆為空筆 目的是使繪製的圖形沒有描邊
    painter.setPen(Qt::NoPen);

    // 繪製一個矩形
    painter.drawRect(2, 2, width() - 4, height() - 4);

    // 計算每個小格子的寬度和高度
    w = width() - 4, h = height() - 4;
    w = (w - 25) / 4, h = (h - 25) / 4;

    /* 構造一個字型
     * 字型名字為Consolas
     * 字型設定為粗體
     * 字型大小為40畫素
     * */
    QFont font;
    font.setFamily("Consolas");
    font.setBold(true);
    font.setPixelSize(40);
    // 使painter應用這個字型
    painter.setFont(font);

    // 迴圈繪製遊戲面板
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
        {
            // 如果方格中有數字
            if (board[i][j])
            {
                // 設定畫刷顏色為數碼對應的顏色
                brush.setColor(digitBkg[getBitCount(board[i][j])]);
                // 應用這個畫刷
                painter.setBrush(brush);
                // 繪製一個小方格
                painter.drawRect(QRectF(7 + (w + 5) * j, 7 + (h + 5) * i, w, h));
                // 設定畫筆為黑色畫筆
                painter.setPen(QColor::fromRgb(0, 0, 0));
                // 繪製數碼
                painter.drawText(QRectF(7 + (w + 5) * j, 7 + (h + 5) * i, w, h), Qt::AlignCenter,
                                 QString::number(board[i][j]));
                // 設定畫筆為空筆
                painter.setPen(Qt::NoPen);
            }
            // 如果方格中沒有數字
            else
            {
                // 設定畫刷顏色為 RGB分量為171 165 141的顏色
                brush.setColor(QColor::fromRgb(171, 165, 141));
                // 應用這個畫刷
                painter.setBrush(brush);
                // 繪製小方格
                painter.drawRect(QRectF(7 + (w + 5) * j, 7 + (h + 5) * i, w, h));
            }
        }
}

void GameWidget::restart()
{
    // 初始化相關變數 同構造函式
    score = 0;
    digitCount = 2;
    memset(board, 0, sizeof(board));
    board[rand() % 4][rand() % 4] = 2;
    while(true) {
        int x = rand() % 4;
        int y = rand() % 4;
        if (board[x][y] != 0) {
            continue;
        }
        else {
            board[x][y] = 2;
            break;
        }
    }
    emit ScoreInc(score);
    update();
}

bool GameWidget::checkGameOver()
{
    // 迴圈檢測是否含有相鄰的相同數碼
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
        {
            if (j != 3 && board[i][j] == board[i][j + 1])
                return false;
            if (i != 3 && board[i][j] == board[i + 1][j])
                return false;
        }
    return true;
}

bool GameWidget::checkWin()
{
    // 迴圈檢測是否某個方格的數字為2048
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 4; j++)
            if (board[i][j] == 2048)
                return true;
    return false;
}

int GameWidget::getBitCount(int n)
{
    // 迴圈獲取數字二進位制位數
    int c = 0;
    while (n >>= 1)
        c++;
    // 返回位數-1
    return c - 1;
}
(5)main.cpp
#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();

    return a.exec();
}

2、執行


四、總結

(1)程式碼有很些不足,比如一行中有4個相同的數只有相加前2個、開始新遊戲沒有動畫等。
(2)可以參考其他的2048的程式碼進行修改更新,此原始碼。
(3)若有建議,請留言,在此先感謝!

相關推薦

Qt2048遊戲原始程式碼來自網路

一、簡介         Qt改寫2048遊戲,在linux系統下找尋android的視覺效果。 二、執行圖         啟動執行圖: 三、詳解 1、程式碼 (1)widget.h #ifndef WIDGET_H #define WIDGET_H #inclu

Qt一log調試日誌

gms 生成文件 _file__ ica 沒有 rest delet mar 排除 一、簡單介紹 近期因調試code時,想了解程序的流程,但苦於沒有一個簡易的日誌記錄,不停使用qDebug打印輸出,而終於提交代碼時得去多次刪除信息打印,有時還會出現新改動

Qt一log除錯日誌

一、簡介       最近因除錯code時,想了解程式的流程,但苦於沒有一個簡易的日誌記錄,不停使用qDebug列印輸出,而最終提交程式碼時得去多次刪除列印資訊,有時還會出現新修改的程式碼分不清是哪些部分。而使用#ifdef _DEBUG又比較煩這套,因此寫了些簡單的日誌

Qt八解析XML檔案

一、簡介         QtXml模組提供了一個讀寫XML檔案的流,解析方法包含DOM和SAX。DOM(Document ObjectModel):將XML檔案表示成一棵樹,便於隨機訪問其中的節點,但消耗記憶體相對多一些。SAX(Simple APIfor XML):一

QtQt樣式表

一、簡介       不斷總結好的樣式表,美化自己的介面(在實際工作中會不斷的更新)。 二、詳解 1、載入樣式表文件 QFile file(":/qss/stylesheet.qss"); file.open(QFile::ReadOnly); QString sty

Qt維碼條形碼解析

一、簡介         二維條碼/二維碼(2-dimensional bar code)是用某種特定的幾何圖形按一定規律在平面(二維方向上)分佈的黑白相間的圖形記錄資料符號資訊的,其應用廣泛,如:產品防偽/溯源、廣告推送、網站連結、資料下載、商品交易、定位/導航、電子憑

Qt八視窗下方彈出提示資訊

一、簡介       在專案中一般都會彈出新的子對話方塊顯示提示資訊,但對於一些因後臺資料變化引發的提示還是在視窗下方彈出提示資訊比較合理。點選按鈕彈出提示資訊,當滑鼠放在提示資訊對話方塊上時,暫停動畫可長時間檢視提示資訊。 二、詳解 1、程式碼 (1)fader

Qt一QLineEdit的新樣式和補全歷史記錄

一、簡介        利用背景圖片設計出QLineEdit新的樣式,起到美化介面的效果,並增加自動補全歷史記錄的功能,就可以作為一個完整的庫。  二、詳解 1、知識點 (1)切換QLineEdit的背景 void InnerLineEdit::setNormal()

QtCentos下Qt結合v4l2實現的視訊顯示

一、簡介        v4l2是針對uvc免驅usb裝置的程式設計框架,主要用於採集usb攝像頭。 可從網上下載最新的原始碼(包括v4l2.c和v4l2.h兩個檔案),本文中修改過。       Qt執行介面如下(動態變化的): 二、詳解 1、準備 (1)插入usb攝

關於心理的種傾向查理·芒格-3

9)回饋傾向人們早就發現,和猿類,猴類,狗類等其他許多認知能力較為低下的物種相同,人類身上也有以德報德,以牙還牙的極端傾向;這種傾向明顯能夠促進有利於成員利益的團體合作。這跟很多社會性的動物的基因程式很相似。回饋傾向是群體生活進行協作的基礎。以牙還牙很正常,很普遍,那以德報怨

關於心理的種傾向查理·芒格-2

5)避免不一致傾向避免不一致傾向實際上就是人天生就害怕改變。同樣是由於人類大腦的生理機制決定的,因為這種傾向可以帶來節省運算空間和能量的好處。這種抗改變模式的形成,可能的原因如下:A) 迅速作出決定對生存來說至關重要,而這種抗改變模式有助於更快的作出決定;     這裡有第四

關於心理的種傾向查理·芒格-4

13)過度樂觀傾向“一個人想要什麼,就會相信什麼。”,人類在進化過程中的生存是非常辛苦的,因此具有樂觀傾向的成員更容易度過難關。因為樂觀的成員在面對困難的時候,更願意積極的想辦法解決,這種長期的進化選擇就造就了人類的樂觀傾向。而過度樂觀傾向我覺得是人類的經驗主義(思維慣性)和

Qt一QT_OpenGL

一、簡介           最近想了解些Qt的OpenGL程式設計,可能以後會使用。Opengl是對2D和3D圖形支援很好,有非常多的優化函式,而且是個跨平臺的開源庫。Qt的Opengl封裝的很好,通過Qt的QGLWidget類,將opengl的函式和Qt的介面結合了

Qt介面自定義

一、簡介       Qt自帶的介面不利於樣式的調整和美化,自定義介面便於設計風格。 二、詳解 1、程式碼 (1)pagenumbercontrol.h #ifndef PAGENUMBERCONTROL_H #define PAGENUMBERCONTROL_H

Qt:拖拽文字圖片

一、簡介        首先選擇窗體顯示風格,接著顯現拖拽效果,文字和圖示都可以作為拖拽的物件,在窗體中的文字圖示可以拖拽到視窗的任意位置,它們在兩個獨立執行的程式間也可相互拖拽(此時是複製一份到拖拽目的程式視窗中),文字拖拽的範圍更廣(須注意字符集的轉換)。本文解決這種

Qt六:TCP和UDP之一

一、簡介        Qt使用QtNetwork模組來進行網路程式設計,提供了一層統一的套接字抽象用於編寫不同層次的網路程式,避免了應用套接字進行網路編的繁瑣(因有時需引用底層作業系統的相關資料結構)。有較底層次的類如QTcpSocket、QTcpServer和QUdp

Java經典編程題50道

ray != println 回文 str stat [0 java amp 一個5位數,判斷它是不是回文數。即12321是回文數,個位與萬位相同,十位與千位相同。 public class Example25 { public static void main(S

ElasticSearch學習筆記 索引詞聚合

ElasticSearch學習筆記之二十五 索引詞聚合 Terms Aggregation(索引詞聚合) Size Document counts are approximate(文件計數是近似值) Shard Size Calc

UART串列埠通訊()--暫存器設定

1.1 通訊的三種基本型別 常用的通訊通常可以分為單工、半雙工、全雙工通訊。 單工就是指只允許一方向另外一方傳送資訊,而另一方不能回傳資訊。比如我們的電視遙控器,我們的收音機廣播等,都是單工通訊技術。 半雙工是指資料可以在雙方之間相互傳播,但是同一時刻只能其中一方發給另外一方,

關於心理的種傾向查理&#183;芒格-2

沖突 環境 國際 data- 優勢 幫助 data 貪婪 必須 5)避免不一致傾向避免不一致傾向實際上就是人天生就害怕改變。相同是由於人類大腦的生理機制決定的。由於這樣的傾向能夠帶來節省運算空間和能量的優點。這樣的抗改變模式的形成,可能的原因例如以下:A) 迅速作出決定對