2048 游戏介绍
-
《2048》在4×4的网格上进行。玩家可以使用上、下、左、右四个方向键移动所有方块。但在部分情形下,某些方向不可移动。游戏开始时,网格上会出现两个数值为2或4的方块。每次移动后,另一个数值为2或4的新方块会随机出现在空方格上。方块会沿着指定的方向滑动,直到被其它方块或网格边缘阻挡。如果两个相同数值的方块碰撞,它们将合并成一个方块,其数值等于两个方块的数值之和。如果三个数值相同的方块碰撞,则只会合并靠近终点方向的两个方块,距起点最近的方块的数值不变。若一行或一列中的方块数值均相同,则沿着该行或该列滑动会合并前两个和后两个方块。在同一移动过程中,新生成的方块不能再与其他方块合并。数值较高的方块会发出柔和的光芒;但随着得分增加,光芒会不断变暗。方块数值都是2的幂,最大为131072。界面右上方的记分牌会记录玩家的分数。玩家的初始分数为零,每当两个方块合并时,分数会增加,得分取决于合并后方块的数值。
-
来源链接: 2048(游戏)维基百科
实现思路
我的实现是,一开始出现两个数字2,随着按下方向键它们会朝向对应方向尽可能地移动。当出现方块移动,或是合并时,都会随机生成一个数字2。尽管我并未严格按照游戏介绍上来做的,但逻辑上基本差不多。
初始化样式
这里我先附上头文件,以便读者更好地理解。
#ifndef GAME_2048_H
#define GAME_2048_H
#include <QMainWindow>
#include <QLabel>
#include <QRandomGenerator>
#include <QTimer>
#include <QMessageBox>
#include <QKeyEvent>
QT_BEGIN_NAMESPACE
namespace Ui {
class Game_2048;
}
QT_END_NAMESPACE
class Game_2048 : public QMainWindow
{
Q_OBJECT
public:
explicit Game_2048(QWidget *parent = nullptr);
~Game_2048();
void randomPosition(); // 随机地向没有样式的某个label加入样式
bool checkGameOver(); // 检查游戏是否结束
QString LabelStyleSheet(const QString &text); // 返回对应样式
void labelMoveUp(); // label向上移动
void labelMoveDown(); // label向下移动
void labelMoveLeft(); // label向左移动
void labelMoveRight(); // label向右移动
void resetGameState(); // 窗口关闭时调用重置
public slots:
void randomInitPosition(); // 初始化生成两个位置
protected:
virtual void keyReleaseEvent(QKeyEvent *event);
private:
Ui::Game_2048 *ui;
QString originalStyleSheet; // 原始label方格样式
QString labelStyleSheet_2; // label方格样式
QString labelStyleSheet_4;
QString labelStyleSheet_8;
QString labelStyleSheet_16;
QString labelStyleSheet_32;
QString labelStyleSheet_64;
QString labelStyleSheet_128;
QString labelStyleSheet_256;
QString labelStyleSheet_512;
QString labelStyleSheet_1024;
QString labelStyleSheet_2048;
QVector<QLabel*> labelVector; // 存储label
QFont ft; // 字体
};
#endif // GAME_2048_H
-
在这里我们先对4×4的的方格布局,我采用的是ui界面结合手写代码的方式,来实现布局,我们先看ui布局吧。
这个布局很简单,但要注意的是,我的逻辑是在每一个frame里面添加一个label来显示数字和颜色,而不是直接在frame上显示哦
-
好了,我们接下来看看我们如何向这16个frame上初始label相关的样式。这里我先初始化从数字2至数字2048的label样式,注意这里未考虑当数字大于2048的情况,我相信在座的各位有这个能力,嘻嘻。接着,我初始化没有数字的方格样式,然后通过一个for循环来找到每一个frame,为这些frame添加一个label,并用labelVector来保存它们,这里我使用的是一维的。这样基本样式就完成好了。
#include "game_2048.h"
#include "ui_game_2048.h"
Game_2048::Game_2048(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::Game_2048)
{
ui->setupUi(this);
// label方格样式
labelStyleSheet_2 = QStringLiteral("background-color: rgb(170, 255, 85); border: 2px solid rgba(0,0,0,0.6)"); // 淡绿色
labelStyleSheet_4 = QStringLiteral("background-color: rgb(85, 85, 255); border: 2px solid rgba(0,0,0,0.6)"); // 淡蓝色
labelStyleSheet_8 = QStringLiteral("background-color: rgb(255, 160, 183); border: 2px solid rgba(0,0,0,0.6)"); // 淡粉色
labelStyleSheet_16 = QStringLiteral("background-color: rgb(255, 255, 85); border: 2px solid rgba(0,0,0,0.6)"); // 淡黄色
labelStyleSheet_32 = QStringLiteral("background-color: rgb(255, 85, 85); border: 2px solid rgba(0,0,0,0.6)"); // 淡红色
labelStyleSheet_64 = QStringLiteral("background-color: rgb(170, 255, 255); border: 2px solid rgba(0,0,0,0.6)"); // 淡青色
labelStyleSheet_128 = QStringLiteral("background-color: rgb(255, 140, 123); border: 2px solid rgba(0,0,0,0.6)"); // 淡橙色
labelStyleSheet_256 = QStringLiteral("background-color: rgb(140, 0, 255); border: 2px solid rgba(0,0,0,0.6)"); // 淡紫色
labelStyleSheet_512 = QStringLiteral("background-color: rgb(85, 0, 0); border: 2px solid rgba(0,0,0,0.6)"); // 淡暗红色
labelStyleSheet_1024 = QStringLiteral("background-color: rgb(0, 255, 191); border: 2px solid rgba(0,0,0,0.6)"); // 淡青绿色
labelStyleSheet_2048 = QStringLiteral("background-color: rgb(255, 255, 100); border: 2px solid rgba(0,0,0,0.6)"); // 淡黄色
originalStyleSheet = QStringLiteral("border:2px solid rgba(0,0,0,0.6)");
// 设置鼠标点击获取焦点
setFocusPolicy(Qt::ClickFocus);
// 设置字体大小
ft.setPointSize(30);
for (qint8 i = 1; i <= 16; i++)
{
QString parentFrameName = QString("frame_%1").arg(i);
QFrame *parentFrame = ui->widget->findChild<QFrame*>(parentFrameName);
QGridLayout *gridLayout = new QGridLayout(parentFrame);
gridLayout->setContentsMargins(0, 0, 0, 0); // 设置边距为零
QLabel *label = new QLabel(parentFrame);
label->setStyleSheet(originalStyleSheet);
label->setFixedSize(116, 116);
label->setAlignment(Qt::AlignCenter);
label->setFont(ft);
labelVector.append(label);
gridLayout->addWidget(label);
parentFrame->setLayout(gridLayout);
}
// 重新开始按钮的样式
ui->reStartButton->setStyleSheet("QPushButton{"
"color: rgb(255, 255, 255);"
"background-color: rgb(109, 109, 109);"
"border-left:4px solid rgb(184, 184, 185);"
"border-top:4px solid rgb(184, 184, 185);"
"border-right:4px solid rgb(88, 88, 88);"
"border-bottom:4px solid rgb(88, 88, 88);"
"}"
"QPushButton:hover{"
"color: rgb(255, 255, 255);"
"background-color: rgb(109, 109, 109);"
"border:5px solid rgb(224, 226, 224);"
"}");
初始随机生成方格
-
初始随机方块。这里很多函数可以使用比如rand()或是srand(),这里笔者使用的是qt里面带有的QRandomGenerator::global()->bounded(n),它会生成0-n(不包含n)的n个整数。
void Game_2048::randomInitPosition()
{
for(auto label : labelVector)
{
label->setStyleSheet(originalStyleSheet);
label->setText("");
}
// 生成两个随机位置
int randomPosition1 = QRandomGenerator::global()->bounded(16); // 生成0-15的随机数字
int randomPosition2 = QRandomGenerator::global()->bounded(16);
while (randomPosition2 == randomPosition1) {
randomPosition2 = QRandomGenerator::global()->bounded(16);
}
// 随机两个位置出现方块
labelVector[randomPosition1]->setStyleSheet(labelStyleSheet_2);
labelVector[randomPosition1]->setText("2");
labelVector[randomPosition2]->setStyleSheet(labelStyleSheet_2);
labelVector[randomPosition2]->setText("2");
}
-
出现移动或合成时,生成方格。这里我实现了一个随机出现数字2 label 的方法,当有移动或合成时,可以调用它。
void Game_2048::randomPosition()
{
bool allSet = true; // 是否所有的label设置了方格样式
for(qint8 i = 0;i < labelVector.length();i++)
{
if(labelVector[i]->styleSheet() == originalStyleSheet)
{
allSet = false;
break;
}
}
if(!allSet)
{
// 生成随机位置
int randomIndex = QRandomGenerator::global()->bounded(labelVector.size());
// 查找没有样式的 label
QLabel *label = labelVector[randomIndex];
while (label->styleSheet() != originalStyleSheet)
{
// 如果 label 已经有样式,则重新生成随机位置
randomIndex = QRandomGenerator::global()->bounded(labelVector.size());
label = labelVector[randomIndex];
}
label->setStyleSheet(labelStyleSheet_2);
label->setText("2");
}
}
处理方向移动逻辑
事实上只要懂了一个方向上逻辑如何处理,那么其他方向就是信手拈来。因此笔者这里仅处理的是右方向键上的逻辑。在Qt中我们需要重写键盘释放虚函数
void keyReleaseEvent(QKeyEvent *event)
。
代码实现
void Game_2048::keyReleaseEvent(QKeyEvent *event) //键盘松开事件
{
if(hasFocus()){
switch(event->key())
{
// 按下上键
case Qt::Key_Up:
labelMoveUp();
return;
// 按下下键
case Qt::Key_Down:
labelMoveDown();
return;
// 按下左键
case Qt::Key_Left:
labelMoveLeft();
return;
// 按下右键
case Qt::Key_Right:
labelMoveRight();
return;
default:
QWidget::keyReleaseEvent(event);
}
}
QWidget::keyReleaseEvent(event);
}
接下来我们来处理按下右方向键的逻辑
- 先设置一个flag来判断是否需要随机生成数字 2 label方格
- 先进入一个while循环,这个循环的目的就是为了所有的方块都移动到最右边,注意这并不合成,仅仅只是移动,
- 然后是进入第二个for循环,这个循环的目的是为了合并,但我这里仅考虑了合并,本来还想再调用第一次while循环让所有的label再一次尽可能右移。有精力的小伙伴可以再加入第一个循环,真正让所有方格向右移动。
- 然后就是简单判断一下是否需要生成一个随机位置和游戏提醒模态框。
- 这些逻辑都挺简单的,我这里都实现了,不过最好这些代码看懂了,自己试着写一遍,不要光拿来看,不动手实现。
// 右移
void Game_2048::labelMoveRight()
{
// 是否随机生成方块
bool isRandomGenerateLabel = false;
/* 第一个循环 */
while (true)
{
// 判断是否有方块移动
bool hasMove = false;
// 循环,所有方块向右移动到右边,不合并
for (qint8 i = 3; i >= 0; i--)
{
qint8 StartCol = i * 4 + 2; // 存储开始的列索引
for (qint8 j = i * 4 + 2; j > StartCol - 3; j--)
{
// 后一个是原始样式就正常移动
if (labelVector[j]->styleSheet() != originalStyleSheet && labelVector[j + 1]->styleSheet() == originalStyleSheet)
{
QString preText = labelVector[j]->text();
labelVector[j + 1]->setStyleSheet(labelVector[j]->styleSheet());
labelVector[j + 1]->setText(preText);
labelVector[j]->setStyleSheet(originalStyleSheet);
labelVector[j]->setText("");
hasMove = true;
isRandomGenerateLabel = true;
}
}
}
// 如果没有方块移动,则退出循环
if (!hasMove)
{
break;
}
}
/* 第二个循环 */
for(qint8 i = 3;i >= 0;i--)
{
qint8 StartCol = i * 4 + 2; // 存储开始的列索引
for(qint8 j = i * 4 + 2;j > StartCol - 3;j--)
{
// 后一个是特殊label样式就判断 j 和 j+1 两个文本是否相等
if(labelVector[j]->text() == (labelVector[j+1]->text()) && labelVector[j]->text() != "")
{
labelVector[j]->setText("");
labelVector[j]->setStyleSheet(originalStyleSheet);
labelVector[j+1]->setText(QString::number((labelVector[j+1]->text()).toInt() * 2));
labelVector[j+1]->setStyleSheet(LabelStyleSheet(labelVector[j+1]->text()));
isRandomGenerateLabel = true;
}
}
}
QTimer::singleShot(150, [this,isRandomGenerateLabel]() {
// 如果没有移动或者合成就不随机生成方块
if(isRandomGenerateLabel)
{
this->randomPosition();
}
});
if(checkGameOver())
{
QMessageBox msgBox;
msgBox.setWindowTitle("游戏");
msgBox.setText("很遗憾游戏结束!<br>请问是否再来一次");
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.setTextFormat(Qt::RichText); // 设置文本格式为富文本
int ret = msgBox.exec();
if(ret == QMessageBox::Ok)
{
for(qint8 i = 0;i < labelVector.length();i++)
{
labelVector[i]->setStyleSheet(originalStyleSheet);
labelVector[i]->setText("");
}
randomInitPosition();
}
}
}
处理游戏结束逻辑
简单来说,其实就是如果没有可以再合成的相邻方格或是没有空格子了,就game over了,说真的这些都挺简单的,难的其实只是如何处理方块移动和合并的逻辑。
// 检查游戏是否结束
bool Game_2048::checkGameOver()
{
// 检查是否还有空的格子
for (int i = 0; i < labelVector.size(); i++)
{
if (labelVector[i]->text() == "")
{
return false;
}
}
// 检查是否还有可以合并的相邻方块
for (int i = 0; i < labelVector.size(); i++)
{
if (i % 4 != 3 && labelVector[i]->text() == labelVector[i + 1]->text())
{
return false;
}
if (i < 12 && labelVector[i]->text() == labelVector[i + 4]->text())
{
return false;
}
}
return true; // 游戏结束
}
项目代码
头文件
#ifndef GAME_2048_H
#define GAME_2048_H
#include <QMainWindow>
#include <QLabel>
#include <QRandomGenerator>
#include <QTimer>
#include <QMessageBox>
#include <QKeyEvent>
QT_BEGIN_NAMESPACE
namespace Ui {
class Game_2048;
}
QT_END_NAMESPACE
class Game_2048 : public QMainWindow
{
Q_OBJECT
public:
explicit Game_2048(QWidget *parent = nullptr);
~Game_2048();
void randomPosition(); // 随机地向没有样式的某个label加入样式
bool checkGameOver(); // 检查游戏是否结束
QString LabelStyleSheet(const QString &text); // 返回对应样式
void labelMoveUp(); // label向上移动
void labelMoveDown(); // label向下移动
void labelMoveLeft(); // label向左移动
void labelMoveRight(); // label向右移动
void resetGameState(); // 窗口关闭时调用重置
public slots:
void randomInitPosition(); // 初始化生成两个位置
protected:
virtual void keyReleaseEvent(QKeyEvent *event);
private:
Ui::Game_2048 *ui;
QString originalStyleSheet; // 原始label方格样式
QString labelStyleSheet_2; // label方格样式
QString labelStyleSheet_4;
QString labelStyleSheet_8;
QString labelStyleSheet_16;
QString labelStyleSheet_32;
QString labelStyleSheet_64;
QString labelStyleSheet_128;
QString labelStyleSheet_256;
QString labelStyleSheet_512;
QString labelStyleSheet_1024;
QString labelStyleSheet_2048;
QVector<QLabel*> labelVector; // 存储label
QFont ft; // 字体
};
#endif // GAME_2048_H
源文件
#include "game_2048.h"
#include "ui_game_2048.h"
Game_2048::Game_2048(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::Game_2048)
{
ui->setupUi(this);
// label方格样式
labelStyleSheet_2 = QStringLiteral("background-color: rgb(170, 255, 85); border: 2px solid rgba(0,0,0,0.6)"); // 淡绿色
labelStyleSheet_4 = QStringLiteral("background-color: rgb(85, 85, 255); border: 2px solid rgba(0,0,0,0.6)"); // 淡蓝色
labelStyleSheet_8 = QStringLiteral("background-color: rgb(255, 160, 183); border: 2px solid rgba(0,0,0,0.6)"); // 淡粉色
labelStyleSheet_16 = QStringLiteral("background-color: rgb(255, 255, 85); border: 2px solid rgba(0,0,0,0.6)"); // 淡黄色
labelStyleSheet_32 = QStringLiteral("background-color: rgb(255, 85, 85); border: 2px solid rgba(0,0,0,0.6)"); // 淡红色
labelStyleSheet_64 = QStringLiteral("background-color: rgb(170, 255, 255); border: 2px solid rgba(0,0,0,0.6)"); // 淡青色
labelStyleSheet_128 = QStringLiteral("background-color: rgb(255, 140, 123); border: 2px solid rgba(0,0,0,0.6)"); // 淡橙色
labelStyleSheet_256 = QStringLiteral("background-color: rgb(140, 0, 255); border: 2px solid rgba(0,0,0,0.6)"); // 淡紫色
labelStyleSheet_512 = QStringLiteral("background-color: rgb(85, 0, 0); border: 2px solid rgba(0,0,0,0.6)"); // 淡暗红色
labelStyleSheet_1024 = QStringLiteral("background-color: rgb(0, 255, 191); border: 2px solid rgba(0,0,0,0.6)"); // 淡青绿色
labelStyleSheet_2048 = QStringLiteral("background-color: rgb(255, 255, 100); border: 2px solid rgba(0,0,0,0.6)"); // 淡黄色
originalStyleSheet = QStringLiteral("border:2px solid rgba(0,0,0,0.6)");
// 设置鼠标点击获取焦点
setFocusPolicy(Qt::ClickFocus);
// 设置字体大小
ft.setPointSize(30);
for (qint8 i = 1; i <= 16; i++)
{
QString parentFrameName = QString("frame_%1").arg(i);
QFrame *parentFrame = ui->widget->findChild<QFrame*>(parentFrameName);
QGridLayout *gridLayout = new QGridLayout(parentFrame);
gridLayout->setContentsMargins(0, 0, 0, 0); // 设置边距为零
QLabel *label = new QLabel(parentFrame);
label->setStyleSheet(originalStyleSheet);
label->setFixedSize(116, 116);
// label->setText(QString("label_%1").arg(i));
label->setAlignment(Qt::AlignCenter);
label->setFont(ft);
labelVector.append(label);
gridLayout->addWidget(label);
parentFrame->setLayout(gridLayout);
}
// 初始化生成两个位置
randomInitPosition();
// 重新开始
connect(ui->reStartButton, &QPushButton::clicked, this, &Game_2048::randomInitPosition);
ui->reStartButton->setStyleSheet("QPushButton{"
"color: rgb(255, 255, 255);"
"background-color: rgb(109, 109, 109);"
"border-left:4px solid rgb(184, 184, 185);"
"border-top:4px solid rgb(184, 184, 185);"
"border-right:4px solid rgb(88, 88, 88);"
"border-bottom:4px solid rgb(88, 88, 88);"
"}"
"QPushButton:hover{"
"color: rgb(255, 255, 255);"
"background-color: rgb(109, 109, 109);"
"border:5px solid rgb(224, 226, 224);"
"}");
}
void Game_2048::randomInitPosition()
{
for(auto label : labelVector)
{
label->setStyleSheet(originalStyleSheet);
label->setText("");
}
// 生成两个随机位置
int randomPosition1 = QRandomGenerator::global()->bounded(16); // 生成0-15的随机数字
int randomPosition2 = QRandomGenerator::global()->bounded(16);
while (randomPosition2 == randomPosition1)
{
randomPosition2 = QRandomGenerator::global()->bounded(16);
}
// 随机两个位置出现方块
labelVector[randomPosition1]->setStyleSheet(labelStyleSheet_2);
labelVector[randomPosition1]->setText("2");
labelVector[randomPosition2]->setStyleSheet(labelStyleSheet_2);
labelVector[randomPosition2]->setText("2");
}
void Game_2048::randomPosition()
{
bool allSet = true; // 是否所有的label设置了方格样式
for(qint8 i = 0;i < labelVector.length();i++)
{
if(labelVector[i]->styleSheet() == originalStyleSheet)
{
allSet = false;
break;
}
}
if(!allSet)
{
// 生成随机位置
int randomIndex = QRandomGenerator::global()->bounded(labelVector.size());
// 查找没有样式的 label
QLabel *label = labelVector[randomIndex];
while (label->styleSheet() != originalStyleSheet)
{
// 如果 label 已经有样式,则重新生成随机位置
randomIndex = QRandomGenerator::global()->bounded(labelVector.size());
label = labelVector[randomIndex];
}
label->setStyleSheet(labelStyleSheet_2);
label->setText("2");
}
}
// 检查游戏是否结束
bool Game_2048::checkGameOver()
{
// 检查是否还有空的格子
for (int i = 0; i < labelVector.size(); i++)
{
if (labelVector[i]->text() == "")
{
return false;
}
}
// 检查是否还有可以合并的相邻方块
for (int i = 0; i < labelVector.size(); i++)
{
if (i % 4 != 3 && labelVector[i]->text() == labelVector[i + 1]->text())
{
return false;
}
if (i < 12 && labelVector[i]->text() == labelVector[i + 4]->text())
{
return false;
}
}
return true; // 游戏结束
}
QString Game_2048::LabelStyleSheet(const QString& text)
{
int num = text.toInt();
switch (num) {
case 4:
return labelStyleSheet_4;
case 8:
return labelStyleSheet_8;
case 16:
return labelStyleSheet_16;
case 32:
return labelStyleSheet_32;
case 64:
return labelStyleSheet_64;
case 128:
return labelStyleSheet_128;
case 256:
return labelStyleSheet_256;
case 512:
return labelStyleSheet_512;
case 1024:
return labelStyleSheet_1024;
case 2048:
return labelStyleSheet_2048;
default:
return QString();
}
}
// 上移
void Game_2048::labelMoveUp()
{
// 是否随机生成方块
bool isRandomGenerateLabel = false;
while (true)
{
// 判断是否有方块移动
bool hasMove = false;
// 循环,所有方块向上移动到上边,不合并
for (qint8 i = 0; i < 4; i++)
{
qint8 StartRow = i + 4; // 存储开始的行索引
for (qint8 j = i + 4; j < StartRow + 12; j += 4)
{
// 上一个是原始样式就正常移动
if (labelVector[j]->styleSheet() != originalStyleSheet && labelVector[j - 4]->styleSheet() == originalStyleSheet)
{
QString preText = labelVector[j]->text();
labelVector[j - 4]->setStyleSheet(labelVector[j]->styleSheet());
labelVector[j - 4]->setText(preText);
labelVector[j]->setStyleSheet(originalStyleSheet);
labelVector[j]->setText("");
isRandomGenerateLabel = true;
hasMove = true;
}
}
}
// 如果没有方块移动,则退出循环
if (!hasMove)
{
break;
}
}
for (qint8 i = 0; i < 4; i++)
{
qint8 StartRow = i + 4; // 存储开始的行索引
for (qint8 j = i + 4; j < StartRow + 12; j += 4)
{
// 上一个是特殊label样式就判断 j 和 j-4 两个文本是否相等
if (labelVector[j]->text() == labelVector[j - 4]->text() && labelVector[j]->text() != "")
{
labelVector[j]->setText("");
labelVector[j]->setStyleSheet(originalStyleSheet);
labelVector[j - 4]->setText(QString::number((labelVector[j - 4]->text()).toInt() * 2));
labelVector[j - 4]->setStyleSheet(LabelStyleSheet(labelVector[j - 4]->text()));
isRandomGenerateLabel = true;
}
}
}
QTimer::singleShot(150, [this,isRandomGenerateLabel]() {
// 如果没有移动或者合成就不随机生成方块
if (isRandomGenerateLabel)
{
this->randomPosition();
}
});
if(checkGameOver())
{
QMessageBox msgBox;
msgBox.setWindowTitle("游戏");
msgBox.setText("很遗憾游戏结束!<br>请问是否再来一次");
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.setTextFormat(Qt::RichText); // 设置文本格式为富文本
msgBox.setWindowFlags(Qt::Dialog | Qt::WindowStaysOnTopHint); // 设置窗口始终在最前
int ret = msgBox.exec();
if(ret == QMessageBox::Ok)
{
for(qint8 i = 0; i < labelVector.length(); i++)
{
labelVector[i]->setStyleSheet(originalStyleSheet);
labelVector[i]->setText("");
}
randomInitPosition();
}
}
}
// // 下移
void Game_2048::labelMoveDown()
{
// 是否随机生成方块
bool isRandomGenerateLabel = false;
while (true)
{
// 判断是否有方块移动
bool hasMove = false;
// 循环,所有方块向下移动到下边,不合并
for (qint8 i = 0; i < 4; i++)
{
qint8 StartRow = i + 8; // 存储开始的行索引
for (qint8 j = i + 8; j > StartRow - 12; j -= 4)
{
// 下一个是原始样式就正常移动
if (labelVector[j]->styleSheet() != originalStyleSheet && labelVector[j + 4]->styleSheet() == originalStyleSheet)
{
QString preText = labelVector[j]->text();
labelVector[j + 4]->setStyleSheet(labelVector[j]->styleSheet());
labelVector[j + 4]->setText(preText);
labelVector[j]->setStyleSheet(originalStyleSheet);
labelVector[j]->setText("");
hasMove = true;
isRandomGenerateLabel = true;
}
}
}
// 如果没有方块移动,则退出循环
if (!hasMove)
{
break;
}
}
for (qint8 i = 0; i < 4; i++)
{
qint8 StartRow = i + 8; // 存储开始的行索引
for (qint8 j = i + 8; j > StartRow - 12; j -= 4)
{
// 下一个是特殊label样式就判断 j 和 j+4 两个文本是否相等
if (labelVector[j]->text() == labelVector[j + 4]->text() && labelVector[j]->text() != "")
{
labelVector[j]->setText("");
labelVector[j]->setStyleSheet(originalStyleSheet);
labelVector[j + 4]->setText(QString::number((labelVector[j + 4]->text()).toInt() * 2));
labelVector[j + 4]->setStyleSheet(LabelStyleSheet(labelVector[j + 4]->text()));
isRandomGenerateLabel = true;
}
}
}
QTimer::singleShot(150, [this,isRandomGenerateLabel]() {
// 如果没有移动或者合成就不随机生成方块
if (isRandomGenerateLabel)
{
this->randomPosition();
}
});
if(checkGameOver())
{
QMessageBox msgBox;
msgBox.setWindowTitle("游戏");
msgBox.setText("很遗憾游戏结束!<br>请问是否再来一次");
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.setTextFormat(Qt::RichText); // 设置文本格式为富文本
int ret = msgBox.exec();
if(ret == QMessageBox::Ok)
{
for(qint8 i = 0;i < labelVector.length();i++)
{
labelVector[i]->setStyleSheet(originalStyleSheet);
labelVector[i]->setText("");
}
randomInitPosition();
}
}
}
// 左移
void Game_2048::labelMoveLeft()
{
// 是否随机生成方块
bool isRandomGenerateLabel = false;
while (true)
{
// 判断是否有方块移动
bool hasMove = false;
// 循环,所有方块向左移动到左边,不合并
for (qint8 i = 0; i < 4; i++)
{
qint8 StartCol = i * 4 + 1; // 存储开始的列索引
for (qint8 j = i * 4 + 1; j < StartCol + 3; j++)
{
// 前一个是原始样式就正常移动
if (labelVector[j]->styleSheet() != originalStyleSheet && labelVector[j - 1]->styleSheet() == originalStyleSheet)
{
QString preText = labelVector[j]->text();
labelVector[j - 1]->setStyleSheet(labelVector[j]->styleSheet());
labelVector[j - 1]->setText(preText);
labelVector[j]->setStyleSheet(originalStyleSheet);
labelVector[j]->setText("");
hasMove = true;
isRandomGenerateLabel = true;
}
}
}
// 如果没有方块移动,则退出循环
if (!hasMove)
{
break;
}
}
for (qint8 i = 0; i < 4; i++)
{
qint8 StartCol = i * 4 + 1; // 存储开始的列索引
for (qint8 j = i * 4 + 1; j < StartCol + 3; j++)
{
// 前一个是特殊label样式就判断 j 和 j-1 两个文本是否相等
if (labelVector[j]->text() == labelVector[j - 1]->text() && labelVector[j]->text() != "")
{
labelVector[j]->setText("");
labelVector[j]->setStyleSheet(originalStyleSheet);
labelVector[j - 1]->setText(QString::number((labelVector[j - 1]->text()).toInt() * 2));
labelVector[j - 1]->setStyleSheet(LabelStyleSheet(labelVector[j - 1]->text()));
isRandomGenerateLabel = true;
}
}
}
QTimer::singleShot(150, [this,isRandomGenerateLabel]() {
// 如果没有移动或者合成就不随机生成方块
if (isRandomGenerateLabel)
{
this->randomPosition();
}
});
if(checkGameOver())
{
QMessageBox msgBox;
msgBox.setWindowTitle("游戏");
msgBox.setText("很遗憾游戏结束!<br>请问是否再来一次");
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.setTextFormat(Qt::RichText); // 设置文本格式为富文本
int ret = msgBox.exec();
if(ret == QMessageBox::Ok)
{
for(qint8 i = 0;i < labelVector.length();i++)
{
labelVector[i]->setStyleSheet(originalStyleSheet);
labelVector[i]->setText("");
}
randomInitPosition();
}
}
}
// 右移
void Game_2048::labelMoveRight()
{
// 是否随机生成方块
bool isRandomGenerateLabel = false;
while (true)
{
// 判断是否有方块移动
bool hasMove = false;
// 循环,所有方块向右移动到右边,不合并
for (qint8 i = 3; i >= 0; i--)
{
qint8 StartCol = i * 4 + 2; // 存储开始的列索引
for (qint8 j = i * 4 + 2; j > StartCol - 3; j--)
{
// 后一个是原始样式就正常移动
if (labelVector[j]->styleSheet() != originalStyleSheet && labelVector[j + 1]->styleSheet() == originalStyleSheet)
{
QString preText = labelVector[j]->text();
labelVector[j + 1]->setStyleSheet(labelVector[j]->styleSheet());
labelVector[j + 1]->setText(preText);
labelVector[j]->setStyleSheet(originalStyleSheet);
labelVector[j]->setText("");
hasMove = true;
isRandomGenerateLabel = true;
}
}
}
// 如果没有方块移动,则退出循环
if (!hasMove)
{
break;
}
}
for(qint8 i = 3;i >= 0;i--)
{
qint8 StartCol = i * 4 + 2; // 存储开始的列索引
for(qint8 j = i * 4 + 2;j > StartCol - 3;j--)
{
// 后一个是特殊label样式就判断 j 和 j+1 两个文本是否相等
if(labelVector[j]->text() == (labelVector[j+1]->text()) && labelVector[j]->text() != "")
{
labelVector[j]->setText("");
labelVector[j]->setStyleSheet(originalStyleSheet);
labelVector[j+1]->setText(QString::number((labelVector[j+1]->text()).toInt() * 2));
labelVector[j+1]->setStyleSheet(LabelStyleSheet(labelVector[j+1]->text()));
isRandomGenerateLabel = true;
}
}
}
QTimer::singleShot(150, [this,isRandomGenerateLabel]() {
// 如果没有移动或者合成就不随机生成方块
if(isRandomGenerateLabel)
{
this->randomPosition();
}
});
if(checkGameOver())
{
QMessageBox msgBox;
msgBox.setWindowTitle("游戏");
msgBox.setText("很遗憾游戏结束!<br>请问是否再来一次");
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.setTextFormat(Qt::RichText); // 设置文本格式为富文本
int ret = msgBox.exec();
if(ret == QMessageBox::Ok)
{
for(qint8 i = 0;i < labelVector.length();i++)
{
labelVector[i]->setStyleSheet(originalStyleSheet);
labelVector[i]->setText("");
}
randomInitPosition();
}
}
}
void Game_2048::keyReleaseEvent(QKeyEvent *event) //键盘松开事件
{
if(hasFocus()){
switch(event->key())
{
// 按下上键
case Qt::Key_Up:
labelMoveUp();
return;
// 按下下键
case Qt::Key_Down:
labelMoveDown();
return;
// 按下左键
case Qt::Key_Left:
labelMoveLeft();
return;
// 按下右键
case Qt::Key_Right:
labelMoveRight();
return;
default:
QWidget::keyReleaseEvent(event);
}
}
QWidget::keyReleaseEvent(event);
}
void Game_2048::resetGameState()
{
// 重置状态
for(qint8 i = 0;i < labelVector.length();i++)
{
labelVector[i]->setStyleSheet(originalStyleSheet);
labelVector[i]->setText("");
}
randomInitPosition();
}
Game_2048::~Game_2048()
{
delete ui;
}
项目链接
- 这里是笔者项目github地址:https://github.com/capp-adocia/Game_2048
注意 这个项目是从github仓库 HMCW https://github.com/capp-adocia/HMCW分离出来的一个小游戏
- 如果有小伙伴进不去或不会用,这里提供了exe文件,供大家免费使用:链接:https://pan.baidu.com/s/1dnQKTzGPWErA7jrappFYlQ?pwd=d69l 提取码:d69l
联系
如果对这些感兴趣,不妨来关注或联系我吧,github和qq都可以哦
- QQ:2636427505
- QQ邮箱:2636427505@qq.com
- bilibili:是KaPa啊
- … …