2048游戏_QT实现-LMLPHP2048游戏_QT实现-LMLPHP

 #ifndef GAMEWIDGET_H
 #define GAMEWIDGET_H

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

 // 手势的方向
 enum GestureDirect
 {
     LEFT = ,   // 向左
     RIGHT = ,  // 向右
     UP = ,     // 向上
     DOWN =     // 向下
 };

 // 定义动画的类型
 enum AnimationType
 {
     MOVE = ,       // 方格移动动画
     APPEARANCE =   // 方格出现动画
 };

 // 动画结构体
 struct Animation
 {
     AnimationType type;     // 动画类型
     GestureDirect direct;   // 方向
     QPointF startPos;       // 起始点坐标 出现动画仅仅使用这个坐标
     QPointF endPos;         // 终止点坐标 移动动画的终点坐标
     int digit;              // 数码
     int digit2;             // 第二数码 数码可能被合并
 };

 // 游戏部件类 继承自QWidget
 class GameWidget : public QWidget
 {
     Q_OBJECT
 public:
     // 构造函数
     );

 private:
     // 游戏面板 存储每个格子的数值
     ][];
     // 数码的个数 存储当前面板上的数字的个数
     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
 #include "GameWidget.h"

 // 颜色数组 存储每个数字对应的背景色
 QColor digitBkg[] = {QColor::fromRgb(0xFF, 0xFF, 0xCC), QColor::fromRgb(0xFF, 0xFF, 0x99),
                             QColor::fromRgb(0xFF, 0xCC, 0xCC), QColor::fromRgb(0xFF, 0xCC, 0x99),
                             QColor::fromRgb(0xFF, 0x99, 0x99), QColor::fromRgb(0xFF, 0x99, 0x66),
                             QColor::fromRgb(0xFF, 0x66, 0x66), QColor::fromRgb(0xCC, 0x99, 0x66),
                             QColor::fromRgb(0xCC, 0x33, 0x33), QColor::fromRgb(0xCC, 0x00, 0x33),
                             QColor::fromRgb(0xFF, 0x00, 0x00)};

 // 每个方向位置的增量
 QPointF dPos[] = {QPointF(-, ), QPointF(, ), QPointF(, -), QPointF(, ), QPointF(-, -)};

 GameWidget::GameWidget(QWidget *parent) :
     QWidget(parent)
 {
     // 连接手势移动信号和相应的槽函数
     connect(this, SIGNAL(GestureMove(GestureDirect)), SLOT(onGestureMove(GestureDirect)));
     // 初始化board数组
     memset(board, , sizeof(board));
     // 随机填入两个2
     board[rand() % ][rand() % ] = ;
     board[rand() % ][rand() % ] = ;
     // 分数初始化为0
     score = ;
     // 数码个数初始化为2
     digitCount = ;
     isAnimating = false;
     cacheImg = NULL;
 }

 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))
     {
         )
             emit GestureMove(LEFT);
         else
             emit GestureMove(RIGHT);
     }
     else
     {
         )
             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:
         // 循环每一行
         ; i < ; i++)
         {
             /* 初始化j k为0
              * 这里j表示要交换的数字列号
              * k表示交换到的位置的列号
              * */
             j = , k = , combine = false;
             while (true)
             {
                 // 循环找到第一个不是0的数字对应的列号
                  && board[i][j] == )
                     j++;
                 // 如果超过了3则说明搜索完毕 推出循环
                 )
                     break;
                 // 交换两个数字
                 qSwap(board[i][k], board[i][j]);
                 // 记录动画信息
                 a.type = MOVE;
                 a.startPos = QPointF( + (w + ) * j,  + (h + ) * i);
                 a.endPos = QPointF( + (w + ) * k,  + (h + ) * i);
                 a.digit = a.digit2 = board[i][k];
                 a.direct = LEFT;
                 //如果交换后的数字与其前一列的数字相同
                  && board[i][k] == board[i][k - ])
                 {
                     // 前一列的数字*2
                     board[i][k - ] <<= ;
                     // 这一列的数字置为0
                     board[i][k] = ;
                     // 记录动画信息
                     a.digit2 = board[i][k - ];
                     a.endPos = QPointF( + (w + ) * (k - ),  + (h + ) * i);
                     // 增加分数
                     score += board[i][k - ];
                     // 发射增加分数的信号
                     emit ScoreInc(score);
                     // 数码个数-1
                     digitCount--;
                     combine = true;
                 }
                 else
                     k++;
                 j++;
                 // 添加到动画链表
                 animationList.append(a);
             }
         }
         break;
         // 其余三个方向与左向类似
     case RIGHT:
         ; i < ; i++)
         {
             j = , k = , combine = false;
             while (true)
             {
                  && board[i][j] == )
                     j--;
                 )
                     break;
                 qSwap(board[i][k], board[i][j]);
                 a.type = MOVE;
                 a.startPos = QPointF( + (w + ) * j,  + (h + ) * i);
                 a.endPos = QPointF( + (w + ) * k,  + (h + ) * i);
                 a.digit = a.digit2 = board[i][k];
                 a.direct = RIGHT;
                  && board[i][k] == board[i][k + ])
                 {
                     board[i][k + ] <<= ;
                     board[i][k] = ;
                     a.digit2 = board[i][k + ];
                     a.endPos = QPointF( + (w + ) * (k + ),  + (h + ) * i);
                     score += board[i][k + ];
                     emit ScoreInc(score);
                     digitCount--;
                     combine = true;
                 }
                 else
                     k--;
                 j--;
                 animationList.append(a);
             }
         }
         break;
     case UP:
         ; i < ; i++)
         {
             j = , k = , combine = false;
             while (true)
             {
                  && board[j][i] == )
                     j++;
                 )
                     break;
                 qSwap(board[k][i], board[j][i]);
                 a.type = MOVE;
                 a.startPos = QPointF( + (w + ) * i,  + (h + ) * j);
                 a.endPos = QPointF( + (w + ) * i,  + (h + ) * k);
                 a.digit = a.digit2 = board[k][i];
                 a.direct = UP;
                  && board[k][i] == board[k - ][i])
                 {
                     board[k - ][i] <<= ;
                     board[k][i] = ;
                     a.digit2 = board[k - ][i];
                     a.endPos = QPointF( + (w + ) * i,  + (h + ) * (k - ));
                     score += board[k - ][i];
                     emit ScoreInc(score);
                     digitCount--;
                     combine = true;
                 }
                 else
                     k++;
                 j++;
                 animationList.append(a);
             }
         }
         break;
     case DOWN:
         ; i < ; i++)
         {
             j = , k = , combine = false;
             while (true)
             {
                  && board[j][i] == )
                     j--;
                 )
                     break;
                 qSwap(board[k][i], board[j][i]);
                 a.type = MOVE;
                 a.startPos = QPointF( + (w + ) * i,  + (h + ) * j);
                 a.endPos = QPointF( + (w + ) * i,  + (h + ) * k);
                 a.digit = a.digit2 = board[k][i];
                 a.direct = DOWN;
                  && board[k][i] == board[k + ][i])
                 {
                     board[k + ][i] <<= ;
                     board[k][i] = ;
                     a.digit2 = board[k + ][i];
                     a.endPos = QPointF( + (w + ) * i,  + (h + ) * (k + ));
                     score += board[k + ][i];
                     emit ScoreInc(score);
                     digitCount--;
                     combine = true;
                 }
                 else
                     k--;
                 j--;
                 animationList.append(a);
             }
         }
         break;
     }
     // 如果数字木有填满
     )
     {
         // 随机产生行号和列号
         i = rand() % , j = rand() % ;
         // 循环直到行和列对应的元素为0
         )
             i = rand() % , j = rand() % ;
         // 填入2
         board[i][j] = (rand() %  + ) * ;
         // 记录动画信息
         a.type = APPEARANCE;
         a.startPos = a.endPos = QPointF( + (w + ) * j,  + (h + ) * i);
         a.startPos += QPointF(w / , h / );
         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();
     // 构造一个QPainter对象
     QPainter painter(cacheImg);
     // 字体
     QFont font;
     font.setFamily("Consolas");
     font.setBold(true);
     font.setPixelSize();
     painter.setFont(font);
     // 标识所有方格动画是否都播放完毕
     bool ok = false;
     while (true)
     {
         // 构造一个画刷 颜色为R G B分量分别为141 121 81的颜色
         QBrush brush(QColor::fromRgb(, , ));
         // 使painter应用这个画刷
         painter.setBrush(brush);

         // 设置画笔为空笔 目的是使绘制的图形没有描边
         painter.setPen(Qt::NoPen);

         // 绘制一个矩形
         painter.drawRect(, , width() - , height() - );

         // 设置画刷颜色为 RGB分量为171 165 141的颜色
         brush.setColor(QColor::fromRgb(, , ));
         // 应用这个画刷
         painter.setBrush(brush);

         // 循环绘制游戏面板
         ; i < ; i++)
             ; j < ; j++)
                 // 绘制小方格
                 painter.drawRect(QRectF( + (w + ) * j,  + (h + ) * 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(, &eventLoop, SLOT(quit()));
         eventLoop.exec();
     }
     // 播放方格出现的动画
     while (!playAnimation(a, painter))
     {
         update();
         QTimer::singleShot(, &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(, , ));
             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(, , ));
             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[];
         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 -  * (a.startPos.x() - a.endPos.x()),
                          h -  * (a.startPos.y() - a.endPos.y())));
         painter.setPen(QColor::fromRgb(, , ));
         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(, , *cacheImg);
         return;
     }

     // 构造一个画刷 颜色为R G B分量分别为141 121 81的颜色
     QBrush brush(QColor::fromRgb(, , ));
     // 使painter应用这个画刷
     painter.setBrush(brush);

     // 设置画笔为空笔 目的是使绘制的图形没有描边
     painter.setPen(Qt::NoPen);

     // 绘制一个矩形
     painter.drawRect(, , width() - , height() - );

     // 计算每个小格子的宽度和高度
     w = width() - , h = height() - ;
     w = (w - ) / , h = (h - ) / ;

     /* 构造一个字体
      * 字体名字为Consolas
      * 字体设置为粗体
      * 字体大小为40像素
      * */
     QFont font;
     font.setFamily("Consolas");
     font.setBold(true);
     font.setPixelSize();
     // 使painter应用这个字体
     painter.setFont(font);

     // 循环绘制游戏面板
     ; i < ; i++)
         ; j < ; j++)
         {
             // 如果方格中有数字
             if (board[i][j])
             {
                 // 设置画刷颜色为数码对应的颜色
                 brush.setColor(digitBkg[getBitCount(board[i][j])]);
                 // 应用这个画刷
                 painter.setBrush(brush);
                 // 绘制一个小方格
                 painter.drawRect(QRectF( + (w + ) * j,  + (h + ) * i, w, h));
                 // 设置画笔为黑色画笔
                 painter.setPen(QColor::fromRgb(, , ));
                 // 绘制数码
                 painter.drawText(QRectF( + (w + ) * j,  + (h + ) * i, w, h), Qt::AlignCenter,
                                  QString::number(board[i][j]));
                 // 设置画笔为空笔
                 painter.setPen(Qt::NoPen);
             }
             // 如果方格中没有数字
             else
             {
                 // 设置画刷颜色为 RGB分量为171 165 141的颜色
                 brush.setColor(QColor::fromRgb(, , ));
                 // 应用这个画刷
                 painter.setBrush(brush);
                 // 绘制小方格
                 painter.drawRect(QRectF( + (w + ) * j,  + (h + ) * i, w, h));
             }
         }
 }

 void GameWidget::restart()
 {
     // 初始化相关变量 同构造函数
     score = ;
     digitCount = ;
     memset(board, , sizeof(board));
     board[rand() % ][rand() % ] = ;
     board[rand() % ][rand() % ] = ;
     emit ScoreInc(score);
     update();
 }

 bool GameWidget::checkGameOver()
 {
     // 循环检测是否含有相邻的相同数码
     ; i < ; i++)
         ; j < ; j++)
         {
              && board[i][j] == board[i][j + ])
                 return false;
              && board[i][j] == board[i + ][j])
                 return false;
         }
     return true;
 }

 bool GameWidget::checkWin()
 {
     // 循环检测是否某个方格的数字为2048
     ; i < ; i++)
         ; j < ; j++)
             )
                 return true;
     return false;
 }

 int GameWidget::getBitCount(int n)
 {
     // 循环获取数字二进制位数
     ;
     )
         c++;
     // 返回位数-1
     ;
 }
05-08 08:37