2048游戏_QT实现


#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 ...
随机推荐
- 疯狂JAVA16课之对象与内存控制
java内存管理分为两个方面:内存分配和内存回收.这里的内存分配特指创建java对象时JVM为该对象在对内存中所分配的内存空间.内存回收指的是当该java对象失去引用,变成垃圾时,JVM的垃圾回收机制 ...
- (转载)哈夫曼编码(Huffman)
转载自:click here 1.哈夫曼编码的起源: 哈夫曼编码是 1952 年由 David A. Huffman 提出的一种无损数据压缩的编码算法.哈夫曼编码先统计出每种字母在字符串里出现的频率, ...
- 12,13 Proxy和Reflect
Proxy和Reflect Proxy(代理) Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种"元编程"(meta programming),即对编程 ...
- C# 获取磁盘剩余空间
drive.TotalFreeSpace单位为bit,根据需要除以1024 drive同时可以可以获取磁盘分区容量等 //单位MB public static long GetHardDiskSpac ...
- ip地址库 新浪,淘宝
原文连接地址:http://www.9958.pw/post/city_ip function getAddressFromIp($ip){ $urlTaobao = 'http://ip.taoba ...
- SpringMVC学习记录1
起因 以前大三暑假实习的时候看到公司用SpringMVC而不是Struts2,老司机告诉我SpringMVC各种方便,各种解耦. 然后我自己试了试..好像是蛮方便的.... 基本上在Spring的基础 ...
- hibernate学习一(hibernate简介与准备)
一.hibernate简介 Hibernate是一个开放源代码的对象-关系映射(Object/Relational Mapping 即 ORM)框架,它对JDBC进行了非常轻量级的对象封装,它将POJ ...
- juery学习6——焦点事件
参考资料 深入理解javascript中的焦点管理:http://www.cnblogs.com/xiaohuochai/p/5874447.html
- leetcode笔记
82. Remove Duplicates from Sorted List II https://leetcode.com/problems/remove-duplicates-from-sorte ...
- 当前Windows群集心跳阀值设置
一.内容描述: WINDOWS群集之间通过心跳检测(HeartBeat)各个节点是否正常在线,微软称此检测为lookalive,检测通过UDP数据包中封装的RPC信息进行传送.默认情况下为每秒检测一次 ...