首页 > 代码库 > 2048游戏_QT实现

2048游戏_QT实现

技术分享技术分享

  1 #ifndef GAMEWIDGET_H  2 #define GAMEWIDGET_H  3   4 #include <QWidget>  5 #include <QMouseEvent>  6 #include <QEventLoop>  7 #include <QTimer>  8 #include <QPainter>  9 #include <QList> 10  11 // 手势的方向 12 enum GestureDirect 13 { 14     LEFT = 0,   // 向左 15     RIGHT = 1,  // 向右 16     UP = 2,     // 向上 17     DOWN = 3    // 向下 18 }; 19  20 // 定义动画的类型 21 enum AnimationType 22 { 23     MOVE = 0,       // 方格移动动画 24     APPEARANCE = 1  // 方格出现动画 25 }; 26  27 // 动画结构体 28 struct Animation 29 { 30     AnimationType type;     // 动画类型 31     GestureDirect direct;   // 方向 32     QPointF startPos;       // 起始点坐标 出现动画仅仅使用这个坐标 33     QPointF endPos;         // 终止点坐标 移动动画的终点坐标 34     int digit;              // 数码 35     int digit2;             // 第二数码 数码可能被合并 36 }; 37  38 // 游戏部件类 继承自QWidget 39 class GameWidget : public QWidget 40 { 41     Q_OBJECT 42 public: 43     // 构造函数 44     explicit GameWidget(QWidget *parent = 0); 45  46 private: 47     // 游戏面板 存储每个格子的数值 48     int board[4][4]; 49     // 数码的个数 存储当前面板上的数字的个数 50     int digitCount; 51     // 分数 存储当前得分 52     int score; 53     // 起始点坐标 存储手势起点坐标 54     QPoint startPos; 55     // 存储所有需要展现的动画 56     QList<Animation> animationList; 57     // 小格子的宽度和高度 58     qreal w, h; 59     // 缓存图像 60     QImage *cacheImg; 61     // 是否在播放动画效果 62     bool isAnimating; 63  64     // 检测游戏是否结束 65     bool checkGameOver(); 66     // 检测游戏是否获胜 67     bool checkWin(); 68     /* 获取一个数字的二进制位数 当然这里获取的不完全是二进制位数 而是对应颜色数组的下标 69     比如 2 对应 0    8 对应 2*/ 70     int getBitCount(int); 71     // 绘制动画效果 72     bool playAnimation(Animation&, QPainter&); 73     // 鼠标按下触发的事件 74     void mousePressEvent(QMouseEvent *); 75     // 鼠标释放触发的时间 76     void mouseReleaseEvent(QMouseEvent *); 77     // 绘制事件 78     void paintEvent(QPaintEvent *); 79  80     // 以下为一些信号 81 signals: 82     // 手势移动信号 83     void GestureMove(GestureDirect); 84     // 分数增加信号 85     void ScoreInc(int); 86     // 游戏结束信号 87     void GameOver(); 88     // 游戏获胜信号 89     void win(); 90  91     // 以下为一些槽函数 92 public slots: 93     // 处理手势移动信号的槽函数 94     void onGestureMove(GestureDirect); 95     // 重新开始的槽函数 96     void restart(); 97  98 }; 99 100 #endif // GAMEWIDGET_H
  1 #include "GameWidget.h"  2   3 // 颜色数组 存储每个数字对应的背景色  4 QColor digitBkg[11] = {QColor::fromRgb(0xFF, 0xFF, 0xCC), QColor::fromRgb(0xFF, 0xFF, 0x99),  5                             QColor::fromRgb(0xFF, 0xCC, 0xCC), QColor::fromRgb(0xFF, 0xCC, 0x99),  6                             QColor::fromRgb(0xFF, 0x99, 0x99), QColor::fromRgb(0xFF, 0x99, 0x66),  7                             QColor::fromRgb(0xFF, 0x66, 0x66), QColor::fromRgb(0xCC, 0x99, 0x66),  8                             QColor::fromRgb(0xCC, 0x33, 0x33), QColor::fromRgb(0xCC, 0x00, 0x33),  9                             QColor::fromRgb(0xFF, 0x00, 0x00)}; 10  11 // 每个方向位置的增量 12 QPointF dPos[5] = {QPointF(-10, 0), QPointF(10, 0), QPointF(0, -10), QPointF(0, 10), QPointF(-2, -2)}; 13  14 GameWidget::GameWidget(QWidget *parent) : 15     QWidget(parent) 16 { 17     // 连接手势移动信号和相应的槽函数 18     connect(this, SIGNAL(GestureMove(GestureDirect)), SLOT(onGestureMove(GestureDirect))); 19     // 初始化board数组 20     memset(board, 0, sizeof(board)); 21     // 随机填入两个2 22     board[rand() % 4][rand() % 4] = 2; 23     board[rand() % 4][rand() % 4] = 2; 24     // 分数初始化为0 25     score = 0; 26     // 数码个数初始化为2 27     digitCount = 2; 28     isAnimating = false; 29     cacheImg = NULL; 30 } 31  32 void GameWidget::mousePressEvent(QMouseEvent *e) 33 { 34     // 获取起点坐标 35     startPos = e->pos(); 36 } 37  38 void GameWidget::mouseReleaseEvent(QMouseEvent *e) 39 { 40     // 如果在播放动画效果则直接退出防止重复产生手势事件 41     if (isAnimating) 42         return; 43     // 根据终点坐标和起点坐标计算XY坐标的增量 44     float dX = (float)(e->pos().x() - startPos.x()); 45     float dY = (float)(e->pos().y() - startPos.y()); 46     // 确定手势方向 47     if (abs(dX) > abs(dY)) 48     { 49         if (dX < 0) 50             emit GestureMove(LEFT); 51         else 52             emit GestureMove(RIGHT); 53     } 54     else 55     { 56         if (dY < 0) 57             emit GestureMove(UP); 58         else 59             emit GestureMove(DOWN); 60     } 61 } 62  63 void GameWidget::onGestureMove(GestureDirect direct) 64 { 65     int i, j, k; 66     Animation a; 67     // 是否合并过方格 68     bool combine = false; 69     // 处理不同方向 70     switch (direct) 71     { 72     // 向左 73     case LEFT: 74         // 循环每一行 75         for (i = 0; i < 4; i++) 76         { 77             /* 初始化j k为0 78              * 这里j表示要交换的数字列号 79              * k表示交换到的位置的列号 80              * */ 81             j = 0, k = 0, combine = false; 82             while (true) 83             { 84                 // 循环找到第一个不是0的数字对应的列号 85                 while (j < 4 && board[i][j] == 0) 86                     j++; 87                 // 如果超过了3则说明搜索完毕 推出循环 88                 if (j > 3) 89                     break; 90                 // 交换两个数字 91                 qSwap(board[i][k], board[i][j]); 92                 // 记录动画信息 93                 a.type = MOVE; 94                 a.startPos = QPointF(7 + (w + 5) * j, 7 + (h + 5) * i); 95                 a.endPos = QPointF(7 + (w + 5) * k, 7 + (h + 5) * i); 96                 a.digit = a.digit2 = board[i][k]; 97                 a.direct = LEFT; 98                 //如果交换后的数字与其前一列的数字相同 99                 if (!combine && k > 0 && board[i][k] == board[i][k - 1])100                 {101                     // 前一列的数字*2102                     board[i][k - 1] <<= 1;103                     // 这一列的数字置为0104                     board[i][k] = 0;105                     // 记录动画信息106                     a.digit2 = board[i][k - 1];107                     a.endPos = QPointF(7 + (w + 5) * (k - 1), 7 + (h + 5) * i);108                     // 增加分数109                     score += board[i][k - 1];110                     // 发射增加分数的信号111                     emit ScoreInc(score);112                     // 数码个数-1113                     digitCount--;114                     combine = true;115                 }116                 else117                     k++;118                 j++;119                 // 添加到动画链表120                 animationList.append(a);121             }122         }123         break;124         // 其余三个方向与左向类似125     case RIGHT:126         for (i = 0; i < 4; i++)127         {128             j = 3, k = 3, combine = false;129             while (true)130             {131                 while (j > -1 && board[i][j] == 0)132                     j--;133                 if (j < 0)134                     break;135                 qSwap(board[i][k], board[i][j]);136                 a.type = MOVE;137                 a.startPos = QPointF(7 + (w + 5) * j, 7 + (h + 5) * i);138                 a.endPos = QPointF(7 + (w + 5) * k, 7 + (h + 5) * i);139                 a.digit = a.digit2 = board[i][k];140                 a.direct = RIGHT;141                 if (!combine && k < 3 && board[i][k] == board[i][k + 1])142                 {143                     board[i][k + 1] <<= 1;144                     board[i][k] = 0;145                     a.digit2 = board[i][k + 1];146                     a.endPos = QPointF(7 + (w + 5) * (k + 1), 7 + (h + 5) * i);147                     score += board[i][k + 1];148                     emit ScoreInc(score);149                     digitCount--;150                     combine = true;151                 }152                 else153                     k--;154                 j--;155                 animationList.append(a);156             }157         }158         break;159     case UP:160         for (i = 0; i < 4; i++)161         {162             j = 0, k = 0, combine = false;163             while (true)164             {165                 while (j < 4 && board[j][i] == 0)166                     j++;167                 if (j > 3)168                     break;169                 qSwap(board[k][i], board[j][i]);170                 a.type = MOVE;171                 a.startPos = QPointF(7 + (w + 5) * i, 7 + (h + 5) * j);172                 a.endPos = QPointF(7 + (w + 5) * i, 7 + (h + 5) * k);173                 a.digit = a.digit2 = board[k][i];174                 a.direct = UP;175                 if (!combine && k > 0 && board[k][i] == board[k - 1][i])176                 {177                     board[k - 1][i] <<= 1;178                     board[k][i] = 0;179                     a.digit2 = board[k - 1][i];180                     a.endPos = QPointF(7 + (w + 5) * i, 7 + (h + 5) * (k - 1));181                     score += board[k - 1][i];182                     emit ScoreInc(score);183                     digitCount--;184                     combine = true;185                 }186                 else187                     k++;188                 j++;189                 animationList.append(a);190             }191         }192         break;193     case DOWN:194         for (i = 0; i < 4; i++)195         {196             j = 3, k = 3, combine = false;197             while (true)198             {199                 while (j > -1 && board[j][i] == 0)200                     j--;201                 if (j < 0)202                     break;203                 qSwap(board[k][i], board[j][i]);204                 a.type = MOVE;205                 a.startPos = QPointF(7 + (w + 5) * i, 7 + (h + 5) * j);206                 a.endPos = QPointF(7 + (w + 5) * i, 7 + (h + 5) * k);207                 a.digit = a.digit2 = board[k][i];208                 a.direct = DOWN;209                 if (!combine && k < 3 && board[k][i] == board[k + 1][i])210                 {211                     board[k + 1][i] <<= 1;212                     board[k][i] = 0;213                     a.digit2 = board[k + 1][i];214                     a.endPos = QPointF(7 + (w + 5) * i, 7 + (h + 5) * (k + 1));215                     score += board[k + 1][i];216                     emit ScoreInc(score);217                     digitCount--;218                     combine = true;219                 }220                 else221                     k--;222                 j--;223                 animationList.append(a);224             }225         }226         break;227     }228     // 如果数字木有填满229     if (digitCount != 16)230     {231         // 随机产生行号和列号232         i = rand() % 4, j = rand() % 4;233         // 循环直到行和列对应的元素为0234         while (board[i][j] != 0)235             i = rand() % 4, j = rand() % 4;236         // 填入2237         board[i][j] = (rand() % 2 + 1) * 2;238         // 记录动画信息239         a.type = APPEARANCE;240         a.startPos = a.endPos = QPointF(7 + (w + 5) * j, 7 + (h + 5) * i);241         a.startPos += QPointF(w / 2, h / 2);242         a.digit = board[i][j];243         // 数码个数加一244         digitCount++;245     }246     else247     {248         // 如果数字填满了 检测游戏是否over249         if (checkGameOver())250             emit GameOver();// 如果游戏over了则发射GameOver信号251     }252 253     // 开始绘制动画效果254     isAnimating = true;255     // 动画列表的迭代器256     QList<Animation>::iterator it;257     // 事件循环 用于延时258     QEventLoop eventLoop;259     // 删除之前的缓存图像260     if (cacheImg)261         delete cacheImg;262     // 建立缓存图像263     cacheImg = new QImage(width(), height(), QImage::Format_ARGB32);264     // 清空图像265     cacheImg->fill(0);266     // 构造一个QPainter对象267     QPainter painter(cacheImg);268     // 字体269     QFont font;270     font.setFamily("Consolas");271     font.setBold(true);272     font.setPixelSize(40);273     painter.setFont(font);274     // 标识所有方格动画是否都播放完毕275     bool ok = false;276     while (true)277     {278         // 构造一个画刷 颜色为R G B分量分别为141 121 81的颜色279         QBrush brush(QColor::fromRgb(141, 121, 81));280         // 使painter应用这个画刷281         painter.setBrush(brush);282 283         // 设置画笔为空笔 目的是使绘制的图形没有描边284         painter.setPen(Qt::NoPen);285 286         // 绘制一个矩形287         painter.drawRect(2, 2, width() - 4, height() - 4);288 289         // 设置画刷颜色为 RGB分量为171 165 141的颜色290         brush.setColor(QColor::fromRgb(171, 165, 141));291         // 应用这个画刷292         painter.setBrush(brush);293 294         // 循环绘制游戏面板295         for (int i = 0; i < 4; i++)296             for (int j = 0; j < 4; j++)297                 // 绘制小方格298                 painter.drawRect(QRectF(7 + (w + 5) * j, 7 + (h + 5) * i, w, h));299 300         // 假设都播放完毕301         ok = true;302 303         // 循环播放每个方格动画304         for (it = animationList.begin(); it != animationList.end(); it++)305             if (!playAnimation(*it, painter))306                 ok = false;307 308         // 刷新部件309         update();310 311         // 全部播放完则退出312         if (ok)313             break;314 315         // 延时5ms316         QTimer::singleShot(5, &eventLoop, SLOT(quit()));317         eventLoop.exec();318     }319     // 播放方格出现的动画320     while (!playAnimation(a, painter))321     {322         update();323         QTimer::singleShot(5, &eventLoop, SLOT(quit()));324         eventLoop.exec();325     }326     //清除所有动画327     animationList.clear();328     //刷新当前部件329     isAnimating = false;330 331     // 检测游戏是否获胜332     if (checkWin())333         emit win();// 如果获胜则发射win信号334 335     update();336 }337 338 bool GameWidget::playAnimation(Animation& a, QPainter& painter)339 {340     bool rtn = false;341     QBrush brush(Qt::SolidPattern);342 343     // 移动方格位置344     if (a.type == MOVE)345     {346         switch (a.direct)347         {348         case LEFT:349             if (a.startPos.x() > a.endPos.x())350                 a.startPos += dPos[LEFT];351             else352                 a.startPos = a.endPos, rtn = true;353             break;354         case RIGHT:355             if (a.startPos.x() < a.endPos.x())356                 a.startPos += dPos[RIGHT];357             else358                 a.startPos = a.endPos, rtn = true;359             break;360         case UP:361             if (a.startPos.y() > a.endPos.y())362                 a.startPos += dPos[UP];363             else364                 a.startPos = a.endPos, rtn = true;365             break;366         case DOWN:367             if (a.startPos.y() < a.endPos.y())368                 a.startPos += dPos[DOWN];369             else370                 a.startPos = a.endPos, rtn = true;371         }372         // 如果方格移动到终点373         if (!rtn)374         {375             brush.setColor(digitBkg[getBitCount(a.digit)]);376             painter.setBrush(brush);377             painter.drawRect(QRectF(a.startPos.x(), a.startPos.y(), w, h));378             painter.setPen(QColor::fromRgb(0, 0, 0));379             painter.drawText(QRectF(a.startPos.x(), a.startPos.y(), w, h), Qt::AlignCenter,380                              QString::number(a.digit));381         }382         else383         {384             brush.setColor(digitBkg[getBitCount(a.digit2)]);385             painter.setBrush(brush);386             painter.drawRect(QRectF(a.startPos.x(), a.startPos.y(), w, h));387             painter.setPen(QColor::fromRgb(0, 0, 0));388             painter.drawText(QRectF(a.startPos.x(), a.startPos.y(), w, h), Qt::AlignCenter,389                              QString::number(a.digit2));390         }391         painter.setPen(Qt::NoPen);392     }393     else394     {395         // 方格出现的动画效果396         if (a.startPos.x() > a.endPos.x())397             a.startPos += dPos[4];398         else399             a.startPos = a.endPos, rtn = true;400         brush.setColor(digitBkg[getBitCount(a.digit)]);401         painter.setBrush(brush);402         painter.drawRect(QRectF(a.startPos.x(), a.startPos.y(),403                          w - 2 * (a.startPos.x() - a.endPos.x()),404                          h - 2 * (a.startPos.y() - a.endPos.y())));405         painter.setPen(QColor::fromRgb(0, 0, 0));406         painter.drawText(QRectF(a.endPos.x(), a.endPos.y(), w, h),407                          Qt::AlignCenter, QString::number(a.digit));408         painter.setPen(Qt::NoPen);409     }410     return rtn;411 }412 413 void GameWidget::paintEvent(QPaintEvent *)414 {415     // 构造一个QPainter对象 使用它来进行绘图416     QPainter painter(this);417 418     // 如果正在播放动画效果则绘制缓存位图419     if (isAnimating)420     {421         painter.drawImage(0, 0, *cacheImg);422         return;423     }424 425     // 构造一个画刷 颜色为R G B分量分别为141 121 81的颜色426     QBrush brush(QColor::fromRgb(141, 121, 81));427     // 使painter应用这个画刷428     painter.setBrush(brush);429 430     // 设置画笔为空笔 目的是使绘制的图形没有描边431     painter.setPen(Qt::NoPen);432 433     // 绘制一个矩形434     painter.drawRect(2, 2, width() - 4, height() - 4);435 436     // 计算每个小格子的宽度和高度437     w = width() - 4, h = height() - 4;438     w = (w - 25) / 4, h = (h - 25) / 4;439 440     /* 构造一个字体441      * 字体名字为Consolas442      * 字体设置为粗体443      * 字体大小为40像素444      * */445     QFont font;446     font.setFamily("Consolas");447     font.setBold(true);448     font.setPixelSize(40);449     // 使painter应用这个字体450     painter.setFont(font);451 452     // 循环绘制游戏面板453     for (int i = 0; i < 4; i++)454         for (int j = 0; j < 4; j++)455         {456             // 如果方格中有数字457             if (board[i][j])458             {459                 // 设置画刷颜色为数码对应的颜色460                 brush.setColor(digitBkg[getBitCount(board[i][j])]);461                 // 应用这个画刷462                 painter.setBrush(brush);463                 // 绘制一个小方格464                 painter.drawRect(QRectF(7 + (w + 5) * j, 7 + (h + 5) * i, w, h));465                 // 设置画笔为黑色画笔466                 painter.setPen(QColor::fromRgb(0, 0, 0));467                 // 绘制数码468                 painter.drawText(QRectF(7 + (w + 5) * j, 7 + (h + 5) * i, w, h), Qt::AlignCenter,469                                  QString::number(board[i][j]));470                 // 设置画笔为空笔471                 painter.setPen(Qt::NoPen);472             }473             // 如果方格中没有数字474             else475             {476                 // 设置画刷颜色为 RGB分量为171 165 141的颜色477                 brush.setColor(QColor::fromRgb(171, 165, 141));478                 // 应用这个画刷479                 painter.setBrush(brush);480                 // 绘制小方格481                 painter.drawRect(QRectF(7 + (w + 5) * j, 7 + (h + 5) * i, w, h));482             }483         }484 }485 486 void GameWidget::restart()487 {488     // 初始化相关变量 同构造函数489     score = 0;490     digitCount = 2;491     memset(board, 0, sizeof(board));492     board[rand() % 4][rand() % 4] = 2;493     board[rand() % 4][rand() % 4] = 2;494     emit ScoreInc(score);495     update();496 }497 498 bool GameWidget::checkGameOver()499 {500     // 循环检测是否含有相邻的相同数码501     for (int i = 0; i < 4; i++)502         for (int j = 0; j < 4; j++)503         {504             if (j != 3 && board[i][j] == board[i][j + 1])505                 return false;506             if (i != 3 && board[i][j] == board[i + 1][j])507                 return false;508         }509     return true;510 }511 512 bool GameWidget::checkWin()513 {514     // 循环检测是否某个方格的数字为2048515     for (int i = 0; i < 4; i++)516         for (int j = 0; j < 4; j++)517             if (board[i][j] == 2048)518                 return true;519     return false;520 }521 522 int GameWidget::getBitCount(int n)523 {524     // 循环获取数字二进制位数525     int c = 0;526     while (n >>= 1)527         c++;528     // 返回位数-1529     return c - 1;530 }

 

2048游戏_QT实现