[Qt 5.6.2] 利用Qt实现一个难度可变的2048小游戏
利用Qt实现一个难度随时可调的2048小游戏
一、游戏简介
1、规则创新
胜利条件:达成2048
难度变化:玩家可以通过调整难度条来控制随机池(2、4、8、16)中各个数出现的概率,同时也会改变分数的加成比例
移动触发:每次移动后会从随机池中按照概率随机选取一个数,将其随机放置在一个空余位置上
分数计算:总分=基础分+加成分,基础分+=合并的数值,加成分+=随机生成的数值*加成比例
2、游戏效果
二、设计思路
先将该项目分为游戏入口、主窗口设计与游戏逻辑三个主要模块,再在这三个模块的基础上继续细分,分割成若干个小的功能模块,最后逐一实现。由MainWindow与GameLogic两个类分别实现主界面的显示功能与游戏的实现逻辑。
程序入口较为简单,故不赘述。
主窗口设计主要实现棋盘数据的显示与颜色变化、分数与难度的显示、检测方向键并连接进入游戏逻辑的入口。
游戏逻辑是整个项目的核心部分,主要实现整个游戏的逻辑判断,根据主窗口提供的难度计算出难度系数与随机池各个数据出现概率。
三、代码分析
文件具体分为gamelogic.cpp、main.cpp与mainwindow.cpp三个.cpp文件,gamelogic.h、mainwindow.h两个.h文件与一个mainwindow.ui文件。
1、程序入口:main.cpp
1 #include "mainwindow.h" 2 #include <QApplication> 3 4 int main(int argc, char *argv[]) 5 { 6 QApplication a(argc, argv); 7 MainWindow w; 8 w.show(); 9 10 return a.exec(); 11 }
如果想进行人员管理与登陆的拓展可改为以下代码,可自行设计login登录规则:
1 #include "mainwindow.h" 2 #include <QApplication> 3 4 int main(int argc, char *argv[]) 5 { 6 QApplication a(argc, argv); 7 MainWindow w; 8 w.show(); 9 10 return a.exec(); 11 }
2、主窗口设计:mainwindow.cpp/.h/.ui
2.1 mainwindow.ui
2.2 mainwindow.h
1 #ifndef MAINWINDOW_H 2 #define MAINWINDOW_H 3 4 #include <QMainWindow> 5 #include <QKeyEvent> 6 #include <QDebug> 7 #include <QMessageBox> 8 #include <QTextBrowser> 9 #include "gamelogic.h" 10 11 namespace Ui { 12 class MainWindow; 13 } 14 15 class MainWindow : public QMainWindow 16 { 17 Q_OBJECT 18 19 public: 20 GameLogic* game; 21 explicit MainWindow(QWidget *parent = 0); 22 ~MainWindow(); 23 void initAll(); 24 void showMessage(); 25 void showBroad(); 26 void keyPressEvent(QKeyEvent *event); 27 void changeColor(QLabel* label, int num); 28 29 private slots: 30 void on_pushButton_start_clicked(); 31 32 void on_pushButton_close_clicked(); 33 34 void on_horizontalSlider_valueChanged(int value); 35 36 private: 37 Ui::MainWindow *ui; 38 }; 39 40 #endif // MAINWINDOW_H
2.3 mainwindow.cpp(代码共177行,折叠)
1 #include "mainwindow.h" 2 #include "ui_mainwindow.h" 3 #include "gamelogic.h" 4 5 MainWindow::MainWindow(QWidget *parent) : 6 QMainWindow(parent), 7 ui(new Ui::MainWindow) 8 { 9 game = new GameLogic; 10 ui->setupUi(this); 11 showBroad(); 12 } 13 14 MainWindow::~MainWindow() 15 { 16 delete ui; 17 } 18 19 void MainWindow::initAll() 20 { 21 game->initAll(); 22 on_horizontalSlider_valueChanged(ui->horizontalSlider->value()); 23 } 24 25 26 void MainWindow::on_pushButton_start_clicked() 27 { 28 ui->pushButton_start->setText("重新开始"); 29 initAll(); 30 31 game->createNum(); 32 game->createNum(); 33 game->setGameStart(true); 34 35 showBroad(); 36 showMessage(); 37 } 38 39 void MainWindow::on_pushButton_close_clicked() 40 { 41 this->close(); 42 } 43 44 void MainWindow::on_horizontalSlider_valueChanged(int value) 45 { 46 value = ui->horizontalSlider->value(); 47 ui->label_showGradeUpCoefficient->setText(QString::number(value*2)+"%"); 48 game->setGradeUpCoefficient((float)ui->horizontalSlider->value()/100); 49 50 if (20 > value) ui->label_showDifficult->setText("简单"); 51 else if(40 > value) ui->label_showDifficult->setText("正常"); 52 else if(60 > value) ui->label_showDifficult->setText("困难"); 53 else if(80 > value) ui->label_showDifficult->setText("地狱"); 54 else ui->label_showDifficult->setText("混沌"); 55 } 56 57 void MainWindow::keyPressEvent(QKeyEvent *event) 58 { 59 if(!game->getGameStart()) return; 60 switch(event->key()) 61 { 62 case Qt::Key_Up: game->process(CMD_UP); break; 63 case Qt::Key_Down: game->process(CMD_DOWN); break; 64 case Qt::Key_Left: game->process(CMD_LEFT); break; 65 case Qt::Key_Right: game->process(CMD_RIGHT); break; 66 } 67 showBroad(); 68 showMessage(); 69 switch(game->judge()) 70 { 71 case STAT_PROCESS: break; 72 case STAT_WIN: QMessageBox::information(NULL,"2048","大吉大利,今晚吃鸡!"); break; 73 case STAT_LOSE: QMessageBox::information(NULL,"2048","再接再厉,永不放弃!"); break; 74 } 75 } 76 77 void MainWindow::showMessage() 78 { 79 ui->label_showGrade->setText(QString::number(game->getGrade())); 80 ui->label_showGradeBasic->setText(QString::number(game->getGradeBasic())); 81 ui->label_showGradeUp->setText(QString::number(game->getGradeUp())); 82 ui->label_showStep->setText(QString::number(game->getStep())); 83 } 84 85 void MainWindow::showBroad() 86 { 87 //第一行 88 if(game->getData(0,0)) ui->label_00->setText(QString::number(game->getData(0,0))); 89 else ui->label_00->setText(" "); 90 if(game->getData(0,1)) ui->label_01->setText(QString::number(game->getData(0,1))); 91 else ui->label_01->setText(" "); 92 if(game->getData(0,2)) ui->label_02->setText(QString::number(game->getData(0,2))); 93 else ui->label_02->setText(" "); 94 if(game->getData(0,3)) ui->label_03->setText(QString::number(game->getData(0,3))); 95 else ui->label_03->setText(" "); 96 changeColor(ui->label_00, game->getData(0,0)); 97 changeColor(ui->label_01, game->getData(0,1)); 98 changeColor(ui->label_02, game->getData(0,2)); 99 changeColor(ui->label_03, game->getData(0,3)); 100 101 //第二行 102 if(game->getData(1,0)) ui->label_10->setText(QString::number(game->getData(1,0))); 103 else ui->label_10->setText(" "); 104 if(game->getData(1,1)) ui->label_11->setText(QString::number(game->getData(1,1))); 105 else ui->label_11->setText(" "); 106 if(game->getData(1,2)) ui->label_12->setText(QString::number(game->getData(1,2))); 107 else ui->label_12->setText(" "); 108 if(game->getData(1,3)) ui->label_13->setText(QString::number(game->getData(1,3))); 109 else ui->label_13->setText(" "); 110 changeColor(ui->label_10, game->getData(1,0)); 111 changeColor(ui->label_11, game->getData(1,1)); 112 changeColor(ui->label_12, game->getData(1,2)); 113 changeColor(ui->label_13, game->getData(1,3)); 114 115 //第三行 116 if(game->getData(2,0)) ui->label_20->setText(QString::number(game->getData(2,0))); 117 else ui->label_20->setText(" "); 118 if(game->getData(2,1)) ui->label_21->setText(QString::number(game->getData(2,1))); 119 else ui->label_21->setText(" "); 120 if(game->getData(2,2)) ui->label_22->setText(QString::number(game->getData(2,2))); 121 else ui->label_22->setText(" "); 122 if(game->getData(2,3)) ui->label_23->setText(QString::number(game->getData(2,3))); 123 else ui->label_23->setText(" "); 124 changeColor(ui->label_20, game->getData(2,0)); 125 changeColor(ui->label_21, game->getData(2,1)); 126 changeColor(ui->label_22, game->getData(2,2)); 127 changeColor(ui->label_23, game->getData(2,3)); 128 129 //第四行 130 if(game->getData(3,0)) ui->label_30->setText(QString::number(game->getData(3,0))); 131 else ui->label_30->setText(" "); 132 if(game->getData(3,1)) ui->label_31->setText(QString::number(game->getData(3,1))); 133 else ui->label_31->setText(" "); 134 if(game->getData(3,2)) ui->label_32->setText(QString::number(game->getData(3,2))); 135 else ui->label_32->setText(" "); 136 if(game->getData(3,3)) ui->label_33->setText(QString::number(game->getData(3,3))); 137 else ui->label_33->setText(" "); 138 changeColor(ui->label_30, game->getData(3,0)); 139 changeColor(ui->label_31, game->getData(3,1)); 140 changeColor(ui->label_32, game->getData(3,2)); 141 changeColor(ui->label_33, game->getData(3,3)); 142 } 143 144 //改变方块的StyleSheet 145 void MainWindow::changeColor(QLabel* label, int num) 146 { 147 label->setAlignment(Qt::AlignCenter); 148 switch (num) 149 { 150 case 2: label->setStyleSheet("background-color: rgb(238,228,218);" 151 "font:bold 75 30pt ""微软雅黑"""); break; 152 case 4: label->setStyleSheet("background-color: rgb(237,224,200);" 153 "font:bold 75 30pt ""微软雅黑"""); break; 154 case 8: label->setStyleSheet("background-color: rgb(242,177,121);" 155 "font:bold 75 30pt ""微软雅黑"""); break; 156 case 16: label->setStyleSheet("background-color: rgb(245,150,100);" 157 "font:bold 75 30pt ""微软雅黑"""); break; 158 case 32: label->setStyleSheet("background-color: rgb(245,125,95);" 159 "font:bold 75 30pt ""微软雅黑"""); break; 160 case 64: label->setStyleSheet("background-color: rgb(245,95,60);" 161 "font:bold 75 30pt ""微软雅黑"""); break; 162 case 128: label->setStyleSheet("background-color: rgb(237,207,114);" 163 "font:bold 75 25pt ""微软雅黑"""); break; 164 case 256: label->setStyleSheet("background-color: rgb(237,204,97);" 165 "font:bold 75 25pt ""微软雅黑"""); break; 166 case 512: label->setStyleSheet("background-color: rgb(237,204,97);" 167 "font:bold 75 25pt ""微软雅黑"""); break; 168 case 1024: label->setStyleSheet("background-color: rgb(237,204,97);" 169 "font:bold 75 20pt ""微软雅黑"""); break; 170 case 2048: label->setStyleSheet("background-color: rgb(237,204,97);" 171 "font:bold 75 20pt ""微软雅黑"""); break; 172 default: label->setStyleSheet("background-color: rgb(238,228,218);" 173 "font:bold 75 40pt ""微软雅黑"""); break; 174 } 175 }
① 初始化所有数据,锁定检测:
void initAll();
② 通过按钮开始或结束游戏,并且可随时调节游戏难度:
void on_pushButton_start_clicked();
void on_pushButton_close_clicked();
void on_horizontalSlider_valueChanged(int value);
③ 显示界面与各类信息,根据各个方块的数值来调整游戏显示画面:
void showMessage();
void showBroad();
void changeColor(QLabel* label, int num);
④ 检测方向键,执行成员变量game的逻辑函数:
void keyPressEvent(QKeyEvent *event);
3、游戏逻辑:gamelogic.cpp/.h
3.1 gamelogic.h
1 #ifndef GAMELOGIC_H 2 #define GAMELOGIC_H 3 4 #include <iostream> 5 6 #define ROW 4 7 #define COL 4 8 9 enum CMD 10 { 11 CMD_UP, 12 CMD_DOWN, 13 CMD_LEFT, 14 CMD_RIGHT, 15 }; 16 17 enum STAT 18 { 19 STAT_WAIT, 20 STAT_PROCESS, 21 STAT_WIN, 22 STAT_LOSE, 23 }; 24 25 class GameLogic 26 { 27 public: 28 GameLogic(); 29 bool createNum(); 30 void process(int cmd); 31 int judge(); 32 void initAll(); 33 void calProb(); 34 35 void moveUp(); 36 void moveDown(); 37 void moveLeft(); 38 void moveRight(); 39 40 bool getGameStart(); 41 void setGameStart(bool); 42 void setGradeUpCoefficient(float); 43 int getData(int,int); 44 int getGrade(); 45 int getGradeBasic(); 46 int getGradeUp(); 47 int getStep(); 48 49 private: 50 bool gameStart; 51 int data[4][4]; 52 float grade; 53 int gradeBasic; 54 float gradeUp; 55 int step; 56 float gradeUpCoefficient; 57 int prob2; 58 int prob4; 59 int prob8; 60 int prob16; 61 }; 62 63 #endif // GAMELOGIC_H
3.2 gamelogic.cpp(代码共299行,折叠)
1 #include "gamelogic.h" 2 #include "ui_mainwindow.h" 3 4 #include <random> 5 #include <iostream> 6 #include <vector> 7 #include <qDebug> 8 using namespace std; 9 10 GameLogic::GameLogic() 11 { 12 initAll(); 13 } 14 15 void GameLogic::initAll() 16 { 17 for(int i=0; i<ROW; i++) 18 { 19 for(int j=0; j<COL; j++) 20 { 21 data[i][j] = 0; 22 } 23 } 24 gameStart = false; 25 grade = 0; 26 gradeBasic = 0; 27 gradeUp = 0; 28 step = 0; 29 gradeUpCoefficient = 0; 30 31 prob2 = 0; 32 prob4 = 0; 33 prob8 = 0; 34 prob16 = 0; 35 } 36 37 void GameLogic::calProb() 38 { 39 qDebug() << "gradeUpCoefficient:" << gradeUpCoefficient ; 40 if(0.6 > gradeUpCoefficient) // 0->0.6 41 { 42 prob2 = 80-gradeUpCoefficient*100*0.5; // 80%->50% 43 prob4 = 100; // 20%->50% 44 prob8 = 0, prob16 = 0; // 0 45 qDebug() << "2:" << prob2 << " 4:" << prob4 << " 8:" << prob8 << " 16:" << prob16 ; 46 } 47 else if(0.8 > gradeUpCoefficient) // 0.6->0.8 48 { 49 prob2 = 80-gradeUpCoefficient*100*0.5; // 50%->40% 50 prob4 = 50+prob2; // 50% 51 prob8 = 100; // 0->10% 52 prob16 = 0; // 0 53 qDebug() << "2:" << prob2 << " 4:" << prob4 << " 8:" << prob8 << " 16:" << prob16 ; 54 } 55 else if(1 >= gradeUpCoefficient)// 0.8->1.0 56 { 57 prob2 = 40; // 40% 58 prob4 = -10+gradeUpCoefficient*100*0.5+prob2; // 30%->40% 59 prob8 = -10+gradeUpCoefficient*100*0.25+prob4; // 10%->15% 60 prob16 = 100; // 0->5% 61 qDebug() << "2:" << prob2 << " 4:" << prob4 << " 8:" << prob8 << " 16:" << prob16 ; 62 } 63 } 64 65 bool GameLogic::createNum() 66 { 67 vector<int> indexSpace; 68 for(int i=0; i<ROW; i++) 69 { 70 for(int j=0; j<COL; j++) 71 { 72 if(!data[i][j]) 73 { 74 indexSpace.push_back(i*4+j); 75 } 76 } 77 } 78 if(!indexSpace.empty()) 79 { 80 int randNum = rand()%100; 81 int randPlace = rand()%indexSpace.size(); 82 int chooseNum = -1; 83 84 calProb(); 85 //qDebug() << "randNum:" << randNum; 86 //qDebug() << "2:" << prob2 << " 4:" << prob4 << " 8:" << prob8 << " 16:" << prob16 ; 87 if (prob2 > randNum) chooseNum = 2; 88 else if(prob4 > randNum) chooseNum = 4; 89 else if(prob8 > randNum) chooseNum = 8; 90 else if(prob16 > randNum) chooseNum = 16; 91 int x = indexSpace[randPlace]/4; 92 int y = indexSpace[randPlace]%4; 93 //qDebug("---------- %d %d----------",x,y); 94 data[x][y] = chooseNum; 95 gradeUp += 2*chooseNum*gradeUpCoefficient; 96 grade = gradeBasic + gradeUp; 97 return true; 98 } 99 else 100 { 101 return false; 102 } 103 } 104 105 void GameLogic::process(int cmd) 106 { 107 switch(cmd) 108 { 109 case CMD_UP: moveUp(); break; 110 case CMD_DOWN: moveDown(); break; 111 case CMD_LEFT: moveLeft(); break; 112 case CMD_RIGHT: moveRight(); break; 113 } 114 createNum(); 115 createNum(); 116 step++; 117 } 118 119 void GameLogic::moveUp() 120 { 121 for(int j=0; j<COL; j++) 122 { 123 //取出所有非零元素 124 int t[10] = {0}; 125 int cnt = 0; 126 for(int i=0; i<ROW; i++) 127 if(data[i][j]) 128 t[cnt++] = data[i][j]; 129 130 for(int k=0; k<cnt-1; k++) 131 { 132 if(t[k]==t[k+1]) 133 { 134 gradeBasic += t[k]; 135 t[k] *= 2; 136 t[k+1] = 0; 137 } 138 } 139 //将合并后的非零元素填入data中 140 int n = 0; 141 for(int k=0; k<cnt; k++) 142 { 143 if(t[k]) 144 data[n++][j] = t[k]; 145 } 146 //尾部补零 147 for(; n<ROW; n++) 148 { 149 data[n][j] = 0; 150 } 151 } 152 } 153 154 void GameLogic::moveDown() 155 { 156 for(int j=0; j<COL; j++) 157 { 158 int t[10]; 159 int cnt = 0; 160 161 for(int i=ROW-1; i>=0; i--) 162 if(data[i][j]) t[cnt++] = data[i][j]; 163 164 for(int k=0; k<cnt-1; k++) 165 { 166 if(t[k]==t[k+1]) 167 { 168 gradeBasic += t[k]; 169 t[k] *= 2; 170 t[k+1] = 0; 171 } 172 } 173 174 int n = ROW-1; 175 for(int k=0; k<cnt; k++) 176 { 177 if(t[k]) data[n--][j] = t[k]; 178 } 179 180 for(; n>=0; n--) 181 { 182 data[n][j] = 0; 183 } 184 } 185 } 186 187 void GameLogic::moveLeft() 188 { 189 for(int i=0; i<ROW; i++) 190 { 191 int t[10]; 192 int cnt = 0; 193 for(int j=0; j<COL; j++) 194 if(data[i][j]) t[cnt++] = data[i][j]; 195 196 for(int k=0; k<cnt-1; k++) 197 { 198 if(t[k]==t[k+1]) 199 { 200 gradeBasic += t[k]; 201 t[k] *= 2; 202 t[k+1] = 0; 203 } 204 } 205 206 int n = 0; 207 for(int k=0; k<cnt; k++) 208 { 209 if(t[k]) data[i][n++] = t[k]; 210 } 211 212 for(; n<COL; n++) 213 { 214 data[i][n] = 0; 215 } 216 217 } 218 } 219 220 void GameLogic::moveRight() 221 { 222 for(int i=0; i<ROW; i++) 223 { 224 int t[10]; 225 int cnt = 0; 226 for(int j=COL-1; j>=0; j--) 227 if(data[i][j]) t[cnt++] = data[i][j]; 228 229 for(int k=0; k<cnt-1; k++) 230 { 231 if(t[k]==t[k+1]) 232 { 233 gradeBasic += t[k]; 234 t[k] *= 2; 235 t[k+1] = 0; 236 } 237 } 238 239 int n = COL-1; 240 for(int k=0; k<cnt; k++) 241 { 242 if(t[k]) data[i][n--] = t[k]; 243 } 244 245 for(; n>=0; n--) 246 { 247 data[i][n] = 0; 248 } 249 250 } 251 } 252 253 int GameLogic::judge() 254 { 255 //赢得游戏 256 for (int i=0; i<ROW; i++) 257 { 258 for (int j=0; j<COL; j++) 259 { 260 if (data[i][j] == 2048) 261 { 262 return STAT_WIN; 263 } 264 } 265 } 266 //横向检查 267 for (int i=0; i<ROW; i++) 268 { 269 for (int j=0; j<COL-1; j++) 270 { 271 if (!data[i][j] || (data[i][j] == data[i][j+1])) 272 { 273 return STAT_PROCESS; 274 } 275 } 276 } 277 //纵向检查 278 for (int j=0; j<COL; j++) 279 { 280 for (int i=0; i<ROW-1; i++) 281 { 282 if (!data[i][j] || (data[i][j] == data[i+1][j])) 283 { 284 return STAT_PROCESS; 285 } 286 } 287 } 288 //不符合上述两种状况,游戏结束 289 return STAT_LOSE; 290 } 291 292 bool GameLogic::getGameStart(){return gameStart;} 293 void GameLogic::setGameStart(bool flag){gameStart = flag;} 294 void GameLogic::setGradeUpCoefficient(float i){gradeUpCoefficient = i;} 295 int GameLogic::getData(int i, int j){return data[i][j];} 296 int GameLogic::getGrade(){return grade;} 297 int GameLogic::getGradeBasic(){return gradeBasic;} 298 int GameLogic::getGradeUp(){return gradeUp;} 299 int GameLogic::getStep(){return step;}
① 宏定义棋盘行列数:#define ROW 4; #define COL 4;
② 枚举定义指令与状态:enum CMD... enum STAT...
③ 定义成员变量:
游戏状态:bool gameStart;
棋盘规格:int data[ROW][COL];
分数统计:float grade;
int gradeBasic;
float gradeUp;
float gradeUpCoefficient;
当前步数:int step;
随机数概率:int prob2;
int prob4;
int prob8;
int prob16;
④ 定义成员函数:
初始化数据:void initAll();
创造随机数:bool createNum();
状态判定:int judge();
概率计算:void calProb();
游戏过程:void process(int cmd);
void moveUp();
void moveDown();
void moveLeft();
void moveRight();
数据处理:bool getGameStart();
void setGameStart(bool);
void setGradeUpCoefficient(float);
int getData(int,int);
int getGrade();
int getGradeBasic();
int getGradeUp();
int getStep();