窗口布局与游戏截图:

实现思路:

1.使用二维数组模拟整个游戏网格,并将二维数组中每个数的大小用作游戏中每个网格中的数据。

2.将对游戏的数据及数据的操作(即玩家的操作对游戏数据的影响)作为一个类,游戏的界面作为一个类,前一个类作为后一个类的成员存在

具体实现:

第一个类:游戏数据与数据操作

类的定义:

class myGame{
public:
myGame();
void InitGame(); // 初始化游戏,清除已有数据,并在4*4的地图中随机在一个空位产生数字2
void CreateNextTwo(); // 随机一个空位,并将这个位置的数字置为2 // 游戏操作,即将整个地图中的数字向上(下、左、右)移动,直到碰到地图边界或者被某个已存
// 在数字的位置挡住,在两个含有相同数字的位置碰撞时,两个数字会叠加作为前面那个位置新的
// 数字,而后一个位置被置为空
void Up();
void Down();
void Left();
void Right(); public:
int data[MODE][MODE]; // 游戏数据,即地图上每个位置存在的数字大小,如果某个位置为空(即没有数字),则用0表示
bool isEmpty[MODE][MODE]; // 用于记录每个位置是否为空
int cntEmpty; // 记录此时游戏中空位置的个数
// 用途:1.当空位置为0时,游戏失败
// 2.用于产生随机数,来确定下一次出现数字2的位置
int score; // 用于计分,计分规则:整个地图中所有数字之和
};

类中函数的具体实现:

myGame::myGame()
{
InitGame();
CreateNextTwo();
} void myGame::InitGame()
{
score = ; // 将得分初始化为0 // 初始化所有位置上的数字,使其全为0
for (int i = ; i < MODE; i++){
for (int j = ; j < MODE; j++){
data[i][j] = ;
}
} // 重置cntEmpty的大小
cntEmpty = MODE * MODE;
} void myGame::CreateNextTwo()
{
srand((unsigned)time(NULL)); // 初始化随机数生成器
int area = rand() % cntEmpty; // 产生下一个随机出现数字的位置
int cnt = ; // 遍历整个二位数组,将随机到的位置上的数字设为2,
for (int i = ; i < MODE; i++){
for (int j = ; j < MODE; j++){
if (data[i][j] == ){
cnt++;
if (area == cnt - ){
data[i][j] = ;
break;
}
}
}
if (area == cnt - ){
break;
}
} // 地图空位减一
cntEmpty--;
score += ; // 总分加2
} void myGame::Up()
{
for (int i = ; i < MODE; i++)
{
for (int j = ; j < MODE; j++)
{
int cnt = j; while (data[j][i] == )
{
cnt++; // 将正在处理的数的下面的数据向上移动
for (int k = j; k < MODE - ; k++)
{
data[k][i] = data[k + ][i];
} // 将最下面的数置为0
data[MODE - ][i] = ; if (cnt >= MODE - )
{
break;
}
} if (j != && data[j][i] == data[j - ][i])
{
data[j - ][i] *= ;
data[j][i] = ; cnt = j; while (data[j][i] == )
{
cnt++; // 将正在处理的数的下面的数据向上移动
for (int k = j; k < MODE - ; k++)
{
data[k][i] = data[k + ][i];
} // 将最下面的数置为0
data[MODE - ][i] = ; if (cnt >= MODE - )
{
break;
}
}
}
}
}
} void myGame::Down()
{
for (int i = ; i < MODE; i++)
{
for (int j = ; j < MODE; j++)
{
int cnt = j; while (data[MODE - - j][i] == )
{
cnt++; // 将此事正在处理的数的上面的数向下平移
for (int k = j; k < MODE - ; k++)
{
data[MODE - - k][i] = data[MODE - - k - ][i];
} // 将最上面的数置为0
data[][i] = ; if (cnt >= MODE - )
{
break;
}
} if (j != && data[MODE - - j][i] == data[MODE -j][i])
{
data[MODE - j][i] *= ;
data[MODE - - j][i] = ; cnt = j; while (data[MODE - - j][i] == )
{
cnt++; // 将此事正在处理的数的上面的数向下平移
for (int k = j; k < MODE - ; k++)
{
data[MODE - - k][i] = data[MODE - - k - ][i];
} // 将最上面的数置为0
data[][i] = ; if (cnt >= MODE - )
{
break;
}
}
}
}
}
} void myGame::Left()
{
for (int i = ; i < MODE; i++)
{
for (int j = ; j < MODE; j++)
{
int cnt = j; // 用于计数 // 当此时处理的数为0时
while (data[i][j] == )
{
cnt++; // 将这个数后面的数向前移动
for (int k = j; k < MODE - ; k++)
{
data[i][k] = data[i][k + ];
} // 将最后面的数置为0
data[i][MODE - ] = ; // 如果处理次数达到最大,退出循环,避免因为一组数据全部为0时导致无限循环
if (cnt >= MODE - )
{
break;
}
} // 当此时处理的数据不是第一个数据,且其与在它前一位的数据相等时
if (j != && data[i][j] == data[i][j - ])
{
// 依据游戏规则,将这两个数合并,并将合并后的值作为前一个数的值,并将后一个数的值置为0
data[i][j - ] *= ;
data[i][j] = ; cnt = j; // 同上一步,处理为0的数据
while (data[i][j] == )
{
cnt++; // 将这个数后面的数向前移动
for (int k = j; k < MODE - ; k++)
{
data[i][k] = data[i][k + ];
} // 将最后面的数置为0
data[i][MODE - ] = ; // 如果处理次数达到最大,退出循环,避免因为一组数据全部为0时导致无限循环
if (cnt >= MODE - )
{
break;
}
}
}
}
}
} void myGame::Right()
{
for (int i = ; i < MODE; i++)
{
for (int j = ; j < MODE; j++)
{
int cnt = j; while (data[i][MODE - - j] == )
{
cnt++; // 将左边的数据向右移动
for (int k = j; k < MODE - ; k++)
{
data[i][MODE - - k] = data[i][MODE - - k - ];
} // 将最左边的数据置为0
data[i][] = ; if (cnt >= MODE - )
{
break;
}
} if (j != && data[i][MODE - - j] == data[i][MODE - j ])
{
data[i][MODE - j] *= ;
data[i][MODE - - j] = ; int cnt = j; while (data[i][MODE - - j] == )
{
cnt++; // 将左边的数据向右移动
for (int k = j; k < MODE - ; k++)
{
data[i][MODE - - k] = data[i][MODE - - k - ];
} // 将最左边的数据置为0
data[i][] = ; if (cnt >= MODE - )
{
break;
}
}
}
}
}
}

第二个类:游戏界面与游戏中的操作

类的定义:

class MainWindow : public QMainWindow
{
Q_OBJECT public:
explicit MainWindow(QWidget *parent = );
~MainWindow(); void ShowGame(); private slots:
void on_pushButton_up_clicked(); void on_pushButton_down_clicked(); void on_pushButton_left_clicked(); void on_pushButton_right_clicked(); void on_actionReset_triggered(); void on_actionQuit_triggered(); private:
Ui::MainWindow *ui;
QTextBrowser *textBrower[MODE][MODE]; myGame game;
};

类的构造函数:

整个界面使用Qt Designer画的

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this); // 关联
textBrower[][] = ui->textBrowser1;
textBrower[][] = ui->textBrowser_2;
textBrower[][] = ui->textBrowser_3;
textBrower[][] = ui->textBrowser_4;
textBrower[][] = ui->textBrowser_5;
textBrower[][] = ui->textBrowser_6;
textBrower[][] = ui->textBrowser_7;
textBrower[][] = ui->textBrowser_8;
textBrower[][] = ui->textBrowser_9;
textBrower[][] = ui->textBrowser_10;
textBrower[][] = ui->textBrowser_11;
textBrower[][] = ui->textBrowser_12;
textBrower[][] = ui->textBrowser_13;
textBrower[][] = ui->textBrowser_14;
textBrower[][] = ui->textBrowser_15;
textBrower[][] = ui->textBrowser_16; ShowGame();
}

信号、槽(对用户操作的反应):

void MainWindow::ShowGame()   // 将整个游戏数据显示到游戏界面
{
for (int i = ; i < MODE; i++)
{
for (int j = ; j < MODE; j++)
{
if (game.data[i][j] != ) // 当不为0时显示数字
{
textBrower[i][j]->setText(QString::number(game.data[i][j]));
}
else // 当为0时不显示
{
textBrower[i][j]->setText(" ");
}
}
} ui->lineEdit->setText(QString::number(game.score));
ui->lineEdit_2->setText(QString::number(MODE));
} // 各个操作
void MainWindow::on_pushButton_up_clicked()
{
game.Up();
game.CreateNextTwo();
ShowGame();
} void MainWindow::on_pushButton_down_clicked()
{
game.Down();
game.CreateNextTwo();
ShowGame();
} void MainWindow::on_pushButton_left_clicked()
{
game.Left();
game.CreateNextTwo();
ShowGame();
} void MainWindow::on_pushButton_right_clicked()
{
game.Right();
game.CreateNextTwo();
ShowGame();
} void MainWindow::on_actionReset_triggered()
{
game.InitGame();
game.CreateNextTwo();
ShowGame();
} void MainWindow::on_actionQuit_triggered()
{
close();
}

总结:

在整个的实现过程中,卡壳的地方主要是对整个数据的上下左右的操作的实现。总是想着一下子将整个操作步骤写完,认为自己

想的实现方法在逻辑上没有问题,那么实现之后也就不会出问题。然而总会发现有着自己没有考虑到的地方,并且程导致序运行

后出现的错误不止如何改正。后来学乖了,老老实实的将整个步骤一步步拆开,一步步的实现。

在写将二维数组的整个数据向某个方向移动时(上述类 myGame中写的游戏操作),虽然移动的方向不同,但整体逻辑是一样的,

并且在每一的方向上的移动,都可以将二维数组拆分为MODE个一维数组,而对于每个一维数组的操作都是相同的,可以先实现

对一维数组的操作,然后在推广到二维数组。代码如下:

void OnePart(int *data)
{
for (int i = ; i < MODE; i++){
int cnt = i; // 为0的情况 while (data[i] == ){
cnt++; for (int j = i; j < MODE - ; j++){
data[j] = data[j + ];
} data[MODE - ] = ; if (cnt >= MODE - ){
break;
}
} // 不为0且此时处理的数不是这一组第一个被处理的数的情况 if (i != && data[i] == data[i - ]){
data[i - ] = * data[i - ];
data[i] = ; cnt = i; while (data[i] == ){
cnt++; for (int k = i; k < - ; k++){
data[k] = data[k + ];
} data[MODE - ] = ; if (cnt >= MODE - ){
break;
}
}
} }
}

用C++、Qt实现的小游戏2048的更多相关文章

  1. c语言----<项目>_小游戏<2048>

    2048 小游戏 主要是针对逻辑思维的一个训练. 主要学习方面:1.随机数产生的概率.2.行与列在进行移动的时候几种情况.3.MessageBox的使用 #include <iostream&g ...

  2. C语言小游戏: 2048.c

    概要:2048.c是一个C语言编写的2048游戏,本文将详细分析它的源码和实现.C语言是一种经典实用的编程语言,本身也不复杂,但是学会C语言和能够编写实用的程序还是有一道鸿沟的.本文试图通过一个例子展 ...

  3. JavaScript小游戏--2048(PC端)

    1.初始化棋局 $(document).ready(function() { prepare_for_mobile(); //适配移动端 new_game(); }); 2.开始新游戏 functio ...

  4. C语言小游戏——2048

      2048   2048这款游戏的玩法很简单,每次可以选择上下左右滑动,每滑动一次,所有的数字方块都会往滑动的方向靠拢,系统也会在空白的地方乱数出现一个数字方块,相同数字的方块在靠拢.相撞时会相加. ...

  5. Qt 贪吃蛇小游戏

    简单的实现了走和变大的样子,剩下的还在完善 贴代码 #include "mainwindow.h" #include "ui_mainwindow.h" #in ...

  6. c++小游戏——2048

    #include <stdio.h> #include <time.h> #include <conio.h> #include <windows.h> ...

  7. JavaScript小游戏--2048(移动端)

    HTML5中新添加了很多事件,但是由于他们的兼容问题不是很理想,应用实战性不是太强,所以在这里基本省略,咱们只分享应用广泛兼容不错的事件,日后随着兼容情况提升以后再陆续添加分享.今天为大家介绍的事件主 ...

  8. JavaScript小游戏--2048(程序流程图)

  9. .NET手撸2048小游戏

    .NET手撸2048小游戏 2048是一款益智小游戏,得益于其规则简单,又和2的倍数有关,因此广为人知,特别是广受程序员的喜爱. 本文将再次使用我自制的"准游戏引擎"FlysEng ...

随机推荐

  1. C#显示及隐藏任务栏

    private const int SW_HIDE = 0; //隐藏任务栏 private const int SW_RESTORE = 9;//显示任务栏 [DllImport("use ...

  2. Knockout v3.4.0 中文版教程-5-计算监控-使用计算监控

    3. 计算监控 1.使用计算监控 如果你有一个监控的属性firstName和另一个lastName,但你想显示全名怎么办? 这就是引入计算监控的原因-这是依赖于一个或多个其他的observables函 ...

  3. 爬虫开发python工具包介绍 (4)

    本文来自网易云社区 作者:王涛 此处我们给出几个常用的代码例子,包括get,post(json,表单),带证书访问:Get 请求 @gen.coroutine def fetch_url():     ...

  4. 【LeetCode】ZigZag Conversion(Z 字形变换)

    这道题是LeetCode里的第6道题. 题目要求: 将一个给定字符串根据给定的行数,以从上往下.从左到右进行 Z 字形排列. 比如输入字符串为 "LEETCODEISHIRING" ...

  5. 【LeetCode】Reverse Nodes in k-Group(k个一组翻转链表)

    这是LeetCode里的第25道题. 题目要求: 给出一个链表,每 k 个节点一组进行翻转,并返回翻转后的链表. k 是一个正整数,它的值小于或等于链表的长度.如果节点总数不是 k 的整数倍,那么将最 ...

  6. Leetcode 332.重新安排行程

    重新安排行程 给定一个机票的字符串二维数组[from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序.所有这些机票都属于一个从JFK(肯尼迪国际机场)出发的先生 ...

  7. shell文件包含

    像其他语言一样,Shell 也可以包含外部脚本,将外部脚本的内容合并到当前脚本. Shell 中包含脚本可以使用: . filename 或 source filename 两种方式的效果相同,简单起 ...

  8. Python2.6.6升级2.7.3

    Python2.7替换2.6: 1.下载Python-2.7.3 #wget http://python.org/ftp/python/2.7.3/Python-2.7.3.tar.bz2 2.解压 ...

  9. 刷题总结——路径(ssoi)

    题目: 题目背景 CF 57D 题目描述 小美今天和她的好朋友在玩捉迷藏游戏.地图可以抽象成一张 n*m 的图,地图上有一些障碍.但这些障碍有一些性质:1:每个障碍周围 8 个格子是没有障碍的.2:每 ...

  10. 洛谷P2625 豪华游轮

    题目描述 有一条豪华游轮(其实就是条小木船),这种船可以执行4种指令: right X : 其中X是一个1到719的整数,这个命令使得船顺时针转动X度. left X : 其中X是一个1到719的整数 ...