不废话,先看效果
![游戏效果](https://i-blog.csdnimg.cn/blog_migrate/be6025c276b9f44b0028644bb8d019e7.gif)
- 基本原理
- 通过按键响应函数
void keyPressEvent(QKeyEvent *event)
实现画面刷新,每次按键后执行操作并且刷新画面。 - 画面刷新通过改变不同区域的样式表。
- 游戏的后台逻辑和原理以及代码:控制台版2048
- qt完整工程:github
- 主界面类:widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include "game.h"
#include <QWidget>
#include <QKeyEvent>
#include "dialog.h"
#include <QLabel>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
Game game;
explicit Widget(QWidget *parent = nullptr);
~Widget();
void SHOW();//打印到屏幕
public slots:
void JiXu();//槽函数,继续游戏
void TuiChu();//槽函数,退出游戏
private:
void keyPressEvent(QKeyEvent *event);//获取输入并执行相应操作
QLabel *lab;
Ui::Widget *ui;
Dialog *dialog;//定义一个对话框成员
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
#include <QKeyEvent>
#include <QDebug>
#include <QString>
#include "game.h"
#include <QMessageBox>
#include <QLabel>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
this->setFocus();
game.Restart();
this->SHOW();
/* 设置对话框 */
dialog =new Dialog(this);
dialog->setModal(true);//模态窗口模式
dialog->setWindowTitle("2048");
dialog->hide();
lab=new QLabel(dialog);
lab->move(70,20);
/*连接信号和槽 */
connect(dialog,SIGNAL(jixu()),this,SLOT(JiXu()));
connect(dialog,SIGNAL(tuichu()),this,SLOT(TuiChu()));
}
Widget::~Widget()
{
delete ui;
}
void Widget::SHOW()
{
//通过改变不同位置的样式表来显示不同的数字
for (int i=0;i<4;i++) {
for (int j=0;j<4;j++) {
int x=i*4+j;
QGraphicsView *p=nullptr;
//选择指针指向位置(第几行第几列)
switch (x) {
case 0: p=ui->p1;break;
case 1: p=ui->p2;break;
case 2: p=ui->p3;break;
case 3: p=ui->p4;break;
case 4: p=ui->p5;break;
case 5: p=ui->p6;break;
case 6: p=ui->p7;break;
case 7: p=ui->p8;break;
case 8: p=ui->p9;break;
case 9: p=ui->p10;break;
case 10: p=ui->p11;break;
case 11: p=ui->p12;break;
case 12: p=ui->p13;break;
case 13: p=ui->p14;break;
case 14: p=ui->p15;break;
case 15: p=ui->p16;break;
default:break;
}
//选择显示的数据
switch(game.BoardList[i][j])
{
case 0:p->setStyleSheet("border-image:url(:/img/0.png)");break;
case 2:p->setStyleSheet("border-image:url(:/img/2.png)");break;
case 4:p->setStyleSheet("border-image:url(:/img/4.png)");break;
case 8:p->setStyleSheet("border-image:url(:/img/8.png)");break;
case 16:p->setStyleSheet("border-image:url(:/img/16.png)");break;
case 32:p->setStyleSheet("border-image:url(:/img/32.png)");break;
case 64:p->setStyleSheet("border-image:url(:/img/64.png)");break;
case 128:p->setStyleSheet("border-image:url(:/img/128.png)");break;
case 256:p->setStyleSheet("border-image:url(:/img/256.png)");break;
case 512:p->setStyleSheet("border-image:url(:/img/512.png)");break;
case 1024:p->setStyleSheet("border-image:url(:/img/1024.png)");break;
case 2048:p->setStyleSheet("border-image:url(:/img/2048.png)");break;
default:break;
}
}
}
}
void Widget::JiXu()
{
game.Restart();
this->SHOW();
this->SHOW();
}
void Widget::TuiChu()
{
this->close();
}
void Widget::keyPressEvent(QKeyEvent *event)
{
//根据按键执行相应的函数
if(event->key()==Qt::Key_Escape)
{
this->close();
}
if(event->key()==Qt::Key_R)
{
game.Restart();
}
if(event->key()==Qt::Key_Up)
{
game.Up();
}
if(event->key()==Qt::Key_Left)
{
game.Left();
}
if(event->key()==Qt::Key_Right)
{
game.Right();
}
if(event->key()==Qt::Key_Down)
{
game.Down();
}
if(game.LOSE())
{
lab->setText("你输了,是否继续?");
dialog->show();
}
if(game.WIN())
{
lab->setText("你赢了,是否继续?");
dialog->show();
}
this->SHOW();
}
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
/*
定义一个对话框,游戏输了或者赢了,弹出
*/
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = nullptr);
~Dialog();
signals:
void jixu();//发送继续游戏信号
void tuichu();//发送退出游戏信号
private slots:
void on_pushButton_2_released();
void on_pushButton_released();
private:
Ui::Dialog *ui;
};
#endif // DIALOG_H
#include "dialog.h"
#include "ui_dialog.h"
#include "widget.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::on_pushButton_2_released()
{
emit this->jixu();
this->hide();
}
void Dialog::on_pushButton_released()
{
emit this->tuichu();
this->hide();
}
#ifndef GAME_H
#define GAME_H
class Game
{
public:
int Scroe = 0;//初始分数
bool ADD = false;//是否添加棋子
int BoardList[4][4] = {};
Game();// 构造函数, 初始化一些参数
bool AddBoard();//添加棋子
void Up();//上下左右按键事件,都类似,以左按键为母版
void Down();
void Right();
void Left();
void Restart();//复位
bool WIN();//判断是否赢了
bool LOSE();//判断是否输了
};
#endif // GAME_H
#include "game.h"
#include <iomanip>
#include <conio.h>
#include <ctime>
Game::Game()
{
}
bool Game::LOSE()
{
bool lose=true;//标志位
//出现0,表示还未结束
for (size_t i = 0; i < 4; i++)
{
for (size_t j = 0; j <4; j++)
{
if (this->BoardList[i][j] == 0)
{
lose = false;
return false;
}
}
}
//横竖相邻元素相等未结束
for (size_t i = 0; i < 4 - 1; i++)
{
for (size_t j = 0; j < 4 - 1; j++)
{
if (this->BoardList[i][j]== this->BoardList[i][j+1]&& this->BoardList[i][j]!=0)
{
return false;
}
if (this->BoardList[j][i]== this->BoardList[j+1][i]&& this->BoardList[j][i]!=0)
{
lose = false;
return false;
}
}
}
if (lose)
{
return true;
}
return false;
}
bool Game::WIN()
{
for (size_t i = 0; i <4; i++)
{
for (int j = 0; j <4; ++j)
{
//出现2048,即为赢
if (this->BoardList[i][j]==2048)
{
return true;
}
}
}
return false;
}
bool Game::AddBoard()
{
int choice[] = { 4, 2, 4, 4, 2, 2, 2, 2 };//可以调节2和4出现的频率
//srand(time(0));
int temp = (rand() % int((sizeof(choice)/sizeof (choice[0]) - 0))) + 0;
int p1 = rand() % 4;
int p2 = rand() % 4;
while (this->BoardList[p1][p2] != 0)//位置已有数字,重新选取
{
p1 = rand() % 4;
p2 = rand() % 4;
}
this->BoardList[p1][p2] = choice[temp];
this->ADD = false;
return true;
}
void Game::Up()
{
for (size_t i = 0; i <4; i++)
{
for (size_t t = 0; t <4; t++)
{
for (size_t j = 0; j <4 - 1; j++)
{
if (this->BoardList[j][i] == 0)
{
if (this->BoardList[j + 1][i]!=0)
{
this->ADD = true;
}
auto temp = this->BoardList[j][i];
this->BoardList[j][i] = this->BoardList[j + 1][i];
this->BoardList[j + 1][i] = temp;
}
}
}
// 2、合并同类项
for (size_t j = 0; j < 4 - 1; j++)
{
// 2 2 2 2 ->4 0 2 2 ->4 0 4 0
// 2 2 2 0 -> 4 0 2 0
if (this->BoardList[j][i] == this->BoardList[j + 1][i])
{
if (this->BoardList[j][i]!=0)
{
this->ADD = true;
}
this->BoardList[j][i] = this->BoardList[j][i] * 2;
this->Scroe = this->BoardList[j][i] + this->Scroe;
this->BoardList[j + 1][i] = 0;
}
}
//3、 再次移动0元素
for (size_t t = 0; t < 4; t++)
{
for (size_t j = 0; j < 4 - 1; j++)
{
// 0 2 0 2 -> 2 2 0 0
if (this->BoardList[j][i] == 0)
{
auto temp = this->BoardList[j][i];
this->BoardList[j][i] = this->BoardList[j + 1][i];
this->BoardList[j + 1][i] = temp;
}
}
}
}
if (this->LOSE() || this->WIN())
{}
else
{
if (this->ADD)
{
this->AddBoard();
this->ADD = false;
}
}
}
void Game::Down()
{
for (size_t i = 0; i < 4; i++)
{
for (size_t t = 4; t>0; t--)
{
for (size_t j = 4 - 1; j >0; j--)
{
if (this->BoardList[j][i] == 0)
{
if (this->BoardList[j - 1][i] != 0)
{
this->ADD = true;
}
auto temp = this->BoardList[j][i];
this->BoardList[j][i] = this->BoardList[j - 1][i];
this->BoardList[j - 1][i] = temp;
}
}
}
// 2、合并同类项
for (size_t j =4 - 1; j>0; j--)
{
// 2 2 2 2 ->4 0 2 2 ->4 0 4 0
// 2 2 2 0 -> 4 0 2 0
if (this->BoardList[j][i] == this->BoardList[j - 1][i])
{
if (this->BoardList[j][i] != 0)
{
this->ADD = true;
}
this->BoardList[j][i] = this->BoardList[j][i] * 2;
this->Scroe = this->BoardList[j][i] + this->Scroe;
this->BoardList[j - 1][i] = 0;
}
}
//3、 再次移动0元素
for (size_t t =4; t >0; t--)
{
for (size_t j =4 - 1; j >0; j--)
{
// 0 2 0 2 -> 2 2 0 0
if (this->BoardList[j][i] == 0)
{
auto temp = this->BoardList[j][i];
this->BoardList[j][i] = this->BoardList[j - 1][i];
this->BoardList[j - 1][i] = temp;
}
}
}
}
if (this->LOSE() || this->WIN())
{}
else
{
if (this->ADD)
{
this->AddBoard();
this->ADD = false;
}
}
}
void Game::Left()
{
for (size_t j= 0; j <4; j++)
{
//1、先移动每行0元素(出现0就交换),最多交换size(this->BoardList)次
for (size_t t = 0; t <4; t++)
{
for (size_t i = 0; i <4 - 1; i++)
{
// 0 2 0 2 ->2 0 2 0
if (this->BoardList[j][i] == 0)
{
// 发生了移动,添加棋子标志位置真
if (this->BoardList[j][i + 1]!=0)
{
this->ADD = true;
}
auto temp = this->BoardList[j][i];
this->BoardList[j][i] = this->BoardList[j][i + 1];
this->BoardList[j][i + 1] = temp;
}
}
}
// 2、合并同类项
for (size_t i = 0; i <4 - 1; i++)
{
// 2 2 2 2 ->4 0 2 2 ->4 0 4 0
// 2 2 2 0 -> 4 0 2 0
if (this->BoardList[j][i]== this->BoardList[j][i+1])
{
// 发生了合并,添加棋子标志位置真
if (this->BoardList[j][i]!=0)
{
this->ADD = true;
}
this->BoardList[j][i] = this->BoardList[j][i] * 2;
this->Scroe = this->BoardList[j][i] + this->Scroe;
this->BoardList[j][i + 1] = 0;
}
}
//3、 再次移动0元素 4 0 2 0 -> 4 2 0 0
for (size_t t = 0; t < 4; t++)
{
for (size_t i = 0; i <4 - 1; i++)
{
// 0 2 0 2 -> 2 2 0 0
if (this->BoardList[j][i] == 0)
{
auto temp = this->BoardList[j][i];
this->BoardList[j][i] = this->BoardList[j][i + 1];
this->BoardList[j][i + 1] = temp;
}
}
}
}
//赢了或者输了就不再添加
if (this->LOSE()||this->WIN())
{}
else
{
if (this->ADD)
{
this->AddBoard();
this->ADD = false;
}
}
}
void Game::Right()
{
for (size_t j = 0; j < 4; j++)
{
//1、先移动每行0元素
for (size_t t = 4; t >0;t--)
{
for (size_t i =4 - 1; i>0; i--)
{
if (this->BoardList[j][i] == 0)
{
if (this->BoardList[j][i - 1]!=0)
{
this->ADD = true;
}
auto temp = this->BoardList[j][i];
this->BoardList[j][i] = this->BoardList[j][i - 1];
this->BoardList[j][i - 1] = temp;
}
}
}
// 2、合并同类项
for (size_t i =4 - 1; i >0; i--)
{
if (this->BoardList[j][i] == this->BoardList[j][i - 1])
{
if (this->BoardList[j][i] != 0)
{
this->ADD = true;
}
this->BoardList[j][i] = this->BoardList[j][i] * 2;
this->Scroe = this->BoardList[j][i] + this->Scroe;
this->BoardList[j][i - 1] = 0;
}
}
//3、 再次移动0元素
for (size_t t =4; t > 0; t--)
{
for (size_t i = 4 - 1; i > 0; i--)
{
// 0 2 0 2 -> 2 2 0 0
if (this->BoardList[j][i] == 0)
{
auto temp = this->BoardList[j][i];
this->BoardList[j][i] = this->BoardList[j][i - 1];
this->BoardList[j][i - 1] = temp;
}
}
}
}
//赢了或者输了就不再添加
if (this->LOSE() || this->WIN())
{}
else
{
if (this->ADD)
{
this->AddBoard();
this->ADD = false;
}
}
}
void Game::Restart()
{
this->Scroe = 0;//初始分数
this->ADD = false;//是否添加棋子
for (size_t i = 0; i < 4; i++)
{
for (size_t j = 0; j < 4; j++)
{
this->BoardList[i][j] =0;
}
}
this->AddBoard();
this->AddBoard();
}