首页 > 代码库 > 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实现
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。