2048游戏_QT实现

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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                     // 前一列的数字*2
102                     board[i][k - 1] <<= 1;
103                     // 这一列的数字置为0
104                     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                     // 数码个数-1
113                     digitCount--;
114                     combine = true;
115                 }
116                 else
117                     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                 else
153                     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                 else
187                     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                 else
221                     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         // 循环直到行和列对应的元素为0
234         while (board[i][j] != 0)
235             i = rand() % 4, j = rand() % 4;
236         // 填入2
237         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     else
247     {
248         // 如果数字填满了 检测游戏是否over
249         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         // 延时5ms
316         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             else
352                 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             else
358                 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             else
364                 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             else
370                 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         else
383         {
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     else
394     {
395         // 方格出现的动画效果
396         if (a.startPos.x() > a.endPos.x())
397             a.startPos += dPos[4];
398         else
399             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      * 字体名字为Consolas
442      * 字体设置为粗体
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             else
475             {
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     // 循环检测是否某个方格的数字为2048
515     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     // 返回位数-1
529     return c - 1;
530 }

 

以上是关于2048游戏_QT实现的主要内容,如果未能解决你的问题,请参考以下文章

Qt 制作2048小游戏

[C++游戏示例]2048-程序员版

Python 2048游戏实现

Qt_扫雷游戏实现

90行代码写一个游戏?教你用90行HasKell代码实现2048游戏

Java实现2048小游戏代码