窗口布局与游戏截图:

实现思路:

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. 深入浅出Oracle:DBA入门、进阶与诊断案例(读书笔记2)

    第5章  Buffer Cache与Shared Pool原理 5.1 Buffer Cache原理 Buffer Cache是Oracle SGA中的一个重要部分,通常的数据访问和修改都需要通过Bu ...

  2. Configure Red Hat Enterprise Linux shared disk cluster for SQL Server

    下面一步一步介绍一下如何在Red Hat Enterprise Linux系统上为SQL Server配置共享磁盘集群(Shared Disk Cluster)及其相关使用(仅供测试学习之用,基础篇) ...

  3. vmware安装CentOS " Intel VT-x 处于禁用状态"

    我刚用虚拟机vmware 11安装CentOS 7 ,出现错误“此主机支持 Intel VT-x,但 Intel VT-x 处于禁用状态”的问题,如下图. Intel VT-x 即Virtualiza ...

  4. Leetcode 406.根据身高重建队列

    根据身高重建队列 假设有打乱顺序的一群人站成一个队列. 每个人由一个整数对(h, k)表示,其中h是这个人的身高,k是排在这个人前面且身高大于或等于h的人数. 编写一个算法来重建这个队列. 注意:总人 ...

  5. 九度oj 题目1214:丑数

    题目描述: 把只包含因子2.3和5的数称作丑数(Ugly Number).例如6.8都是丑数,但14不是,因为它包含因子7. 习惯上我们把1当做是第一个丑数.求按从小到大的顺序的第N个丑数. 输入: ...

  6. 九度oj 题目1499:项目安排

    题目描述: 小明每天都在开源社区上做项目,假设每天他都有很多项目可以选,其中每个项目都有一个开始时间和截止时间,假设做完每个项目后,拿到报酬都是不同的.由于小明马上就要硕士毕业了,面临着买房.买车.给 ...

  7. KS求有向图强连通分量模板

    #include<bits/stdc++.h> using namespace std; typedef long long ll; int n,m; ; *maxn; struct no ...

  8. 【2018.11.23】2018WCTest(7)

    向已退役学长致敬! 题目&他的题解 T1 一道睿智题目,正常思路就是时空复杂度均为 $O(n\times 32768)$ 的背包.这个做法不被卡时间却被卡空间,其实就是想让你离线处理询问,然后 ...

  9. concurrentHashMap面试题

    面试题: ConcurrentHashMap有哪些构造函数? ConcurrentHashMap使用什么技术来保证线程安全? ConcurrentHashMap的get方法是否要加锁,为什么? Con ...

  10. 栅格网络流(cogs 750)

    [问题描述] Bob 觉得一般图的最大流问题太难了,他不知道如何解决,于是他想尝试一个简单点的:栅格网络中的最大流问题,这个虽说简单了一点,但对 Bob 来说依旧太难,现在他有个麻烦需要你帮忙:给你一 ...