#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 ; }
2048游戏_QT实现的更多相关文章
-
用javascript实现一个2048游戏
早就想自己写一个2048游戏了,昨晚闲着没事,终于写了一个 如下图,按方向键开始玩吧. 如果觉得操作不方便,请直接打开链接玩吧: http://gujianbo.1kapp.com/2048/2048 ...
-
powershell字符界面的,powershell加WPF界面的,2048游戏
------[序言]------ 1 2048游戏,有段时间很火,我在地铁上看有人玩过.没错,坐地铁很无聊,人家玩我就一直盯着看. 2 我在电脑上找了一个,试玩了以下,没几次格子就满了.我就气呼呼的放 ...
-
[python] python实现2048游戏,及代码解析。
我初学python,有不对之处望大家指教.转载请征得同意. 我在网络上也找了一些2048游戏代码的讲解,但都不是特别详细.所以我希望能够尽量详细的讲解.同时,有的地方我也不懂,希望大家能帮助补充.我会 ...
-
Android项目开发实战-2048游戏
<2048>是一款比较流行的数字游戏,最早于2014年3月20日发行.原版2048首先在GitHub上发布,原作者是Gabriele Cirulli,后被移植到各个平台.这款游戏是基于&l ...
-
对弈类游戏的人工智能(5)--2048游戏AI的解读
前言: 闲得没事, 网上搜"游戏AI", 看到一篇<<2048游戏的最佳算法是?来看看AI版作者的回答>>的文章. 而这篇文章刚好和之前讲的对弈类游戏AI对 ...
-
最少javascript代码完成一个2048游戏
原生javascript代码写的2048游戏.建议在谷歌浏览器下跑.'WASD'控制方向.演示地址请移步:http://runjs.cn/detail/bp8baf8b 直接贴代码~ html: &l ...
-
cocos2d-x游戏开发实战原创视频讲座系列1之2048游戏开发
cocos2d-x游戏开发实战原创视频讲座系列1之2048游戏开发 的产生 视持续更新中.... 视频存放地址例如以下:http://ipd.pps.tv/user/1058663622 ...
-
用Python做2048游戏 网易云课堂配套实验课。通过GUI来体验编程的乐趣。
第1节 认识wxpython 第2节 画几个形状 第3节 再做个计算器 第4节 最后实现个2048游戏 实验1-认识wxpython 一.实验说明 1. 环境登录 无需密码自动登录,系统用户名shiy ...
-
一个用 C 语言写的迷你版 2048 游戏,仅仅有 500个字符
Jay Chan 用 C 语言写的一个迷你版 2048 游戏,仅仅有 487 个字符. 来围观吧 M[16],X=16,W,k;main(){T(system("stty cbreak&qu ...
随机推荐
-
HDU 1556 线段树或树状数组,插段求点
1.HDU 1556 Color the ball 区间更新,单点查询 2.题意:n个气球,每次给(a,b)区间的气球涂一次色,问最后每个气球各涂了几次. (1)树状数组 总结:树状数组是一个查 ...
-
C# 检测操作系统是否空闲,实现系统空闲后做一些操作
public class CheckComputerFreeState { /// <summary> /// 创建结构体用于返回捕获时间 /// </summary> [St ...
-
js控制只能输入数字
onkeyup=clearNoNum(this) function clearNoNum(obj) { obj.value = obj.value.replace(/[^\d.]/g," ...
-
VMware虚拟机扩容
作者:金良(golden1314521@gmail.com) csdn博客:http://blog.csdn.net/u012176591 用了一段Linux虚拟机.发现安装虚拟机时分配的空间不够用, ...
-
css笔记——小图标文字对齐中级解决方案
出处:http://www.zhangxinxu.com/study/201603/icon-text-vertical-align.html css .icon { display: inline- ...
-
遍历、显示ftp下的文件夹和文件信息
今天做了通过ftp读取ftp根目录下的所有文件夹和文件,嵌套文件夹查询,总共用到了一下代码: 1.FtpFile_Directory package com.hs.dts.web.ftp; impor ...
-
oracle 表查询(2)
使用逻辑操作符号 问题:查询工资高于500 或者是岗位为MANAGER 的雇员,同时还要满足他们的姓名首字母为大写的J? or job = 'MANAGER') and ename LIKE 'J%' ...
-
WEB 小案例 -- 网上书城(一)
距离上次写博客有两周了吧,最多的原因就是自己期末考试了,上课没听就只能在期末狠狠的复习了,毕竟已经挂科了.当然还是因为自己懒吧!!!废话不多说开始我们今天的正题,网上书城! 一. 新建数据表(MySQ ...
-
opencart分类筛选逻辑修改为并且条件
opencart分类筛选模式默认是或的逻辑,满足条件1或条件2都展现出来,如果想要改成既满足条件1又满足条件2要怎么改呢?有一个插件可以实现,FixFilter OC2x,可以修改默认的筛选条件 1. ...
-
JAVA 重写equals和重写hashCode
面试官可能会问你:“你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法?” 首先你需要了解: hashCode()的作用是获取哈希码(散列码) 它实 ...