当前位置: 首页 > news >正文

C++实现图形化2048小游戏

目录

  • 一、游戏规则
  • 二、步骤实现
  • (一) SDL库的安装
  • (二) 初始化游戏界面
    • 1. 后台数字模型
    • 2 显示模型
      • 2.1 SDL库的使用
        • 2.1.1 窗口渲染
        • 2.1.2 矩形绘制
      • 2.2 SDL-ttf库的使用
        • 2.2.1 设置字体属性
        • 2.2.2 创建纹理图层
        • 2.2.3 绘制文字
  • (三) 随机生成2个数字(2或4)
  • (四) 捕获用户方向键操作
    • 1.合并运算函数
  • (五) 更新游戏界面
  • (六) 判断游戏结束
    • 1.判断获胜
    • 2.判断失败
  • 三、总结
  • 四、源码下载

在这里插入图片描述

一、游戏规则

2048游戏开始时,棋盘内会随机出现2到3个数字,这2到3个数字通常是2或4。玩家可以通过方向键来控制棋盘上的数字方块向该方向移动,每次相同的数字方块会进行合并,新的数值为原来2个方块数值之和。游戏的目标是得出最高数2048这个数,一旦所有格子都被填满且未达到2048这个目标,游戏就失败了。

游戏的设计思路:

  1. 初始化游戏界面
  2. 随机生成2个数字(2或4)
  3. 捕获用户方向键操作
  4. 更新游戏界面
  5. 判断游戏结束

二、步骤实现

(一) SDL库的安装

需要SDL库、SDL-ttf库,在linux系统中安装命令apt install libsdl2-devapt install libsdl2-ttf-dev即可安装。

(二) 初始化游戏界面

SDL库使用的顺序是生成窗口 → \to 生成渲染器 → \to 生成纹理 → \to 纹理拷贝至渲染器 → \to 显示。

SDL-ttf库使用的顺序是打开字库 → \to 设置字体属性 → \to 创建表面 → \to 创建纹理。

我希望游戏的格子是4X4的,金黄和橙色相间,而格子里的不同数字也使用不同的颜色渲染(初步设计成3色)。

知识点:类、类外函数定义。

class Chess2048 {private:int gameBoard[16] = {}; //4X4棋盘格SDL_Renderer * render; // 内置窗口渲染器指针TTF_Font *font; //内置字体指针TextSize textSize[11]; //2、4、8...2048的字体尺寸SDL_Texture *texture[11]; //2、4、8...2048的字体纹理指针SDL_Color bgColor[2] = {{255,255,0,255}, {255,180,0,255}}; //金黄、橙SDL_Color fgColor[3] = {{25, 160, 0, 255}, {85, 200, 200, 255}, {55, 0, 100, 255}}; //绿、天蓝、紫public:Chess2048(SDL_Renderer * , TTF_Font *); //构造~Chess2048(); //析构//棋盘移动操作bool up();bool down();bool left();bool right();//判断是否满格bool full();//判断是否胜利bool win();//显示函数void show(int);
};

1. 后台数字模型

如上段代码,其中int gameBoard[16] = {}; 就是后台的棋盘数字4X4的模型,其他的成员都跟图像显示有关。

2 显示模型

知识点:SDL库的使用、SDL-ttf库的使用。

2.1 SDL库的使用

2.1.1 窗口渲染

在main函数中进行SDL初始化操作,如下:

//初始化SDL成视频模式
SDL_Init(SDL_INIT_VIDEO);
//初始化窗口,位置(x,y)默认,尺寸800X800,窗口为显示模式
SDL_Window *window = SDL_CreateWindow("2048小游戏", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 4*DIM, 4*DIM, SDL_WINDOW_SHOWN);
if (!window) {cout << "窗口生成异常" << endl;return 1;
}
//初始化窗口渲染器,相当于画布,-1表示默认的渲染设备,使用软件渲染
SDL_Renderer *render = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE);
if (!render) {cout << "渲染器生成异常" << endl;return 1;
}

生成了窗口渲染器后传递给棋盘类的构造函数,还有1个参数ttf,在2.2部分会讲:
Chess2048 chess = Chess2048(render, ttf);
这里另外涉及1个全局变量DIM,设置成你想要的尺寸就行,我这里定义为200,代表棋盘上1个格子的边长。

2.1.2 矩形绘制

棋盘总共是4X4个格子,所以要绘制16个格子,而每个格子的颜色是相间的,所以这里用到了SDL_Color bgColor[2] = {{255,255,0,255}, {255,180,0,255}}; //金黄、橙这个变量。矩形绘制是在show成员函数中实现的:

//param:interval ms刷新频率
void Chess2048::show(int interval) {//设置背景色白色SDL_SetRenderDrawColor(render, 255, 255, 255, 255); //清空渲染器SDL_RenderClear(render);for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {// cout << this->gameBoard[i*4+j] << " ";//命令行显示//绘制格子SDL_Rect gridRect = {j*DIM, i*DIM, DIM, DIM}; //格子矩形SDL_SetRenderDrawColor(this->render, this->bgColor[(i+j)%2].r, this->bgColor[(i+j)%2].g, this->bgColor[(i+j)%2].b, this->bgColor[(i+j)%2].a);SDL_RenderFillRect(this->render, &gridRect);//绘制数字部分//...}// cout << endl; //命令行显示}SDL_RenderPresent(this->render); //显示SDL_Delay(interval); //延时
}

2.2 SDL-ttf库的使用

在main函数中进行SDL-ttf初始化操作,并传递给棋盘类的构造函数,其中相应的字体我提前拷贝到了程序文件夹,否则就会找不到字体:

//初始化字体
TTF_Init();
//打开字库
TTF_Font *ttf = TTF_OpenFont("NotoSansTamil-Regular.ttf", 50);
if (!ttf) {cout << "字体打开失败" << endl;return 1;
}
Chess2048 chess = Chess2048(render, ttf);
2.2.1 设置字体属性

在构造函数中设置字体加粗属性,当然还有其他属性比如描边等,大家可以自行探索:
TTF_SetFontStyle(font, TTF_STYLE_BOLD);

2.2.2 创建纹理图层

同样是在构造函数中完成的,因为程序一直要用到2、4、8…2048这11个数字,所以我把它设置成了成员数组保存,而我又需要3种颜色,于是随便设置了3种颜色:

private:TextSize textSize[11]; //2、4、8...2048的字体尺寸SDL_Texture *texture[11]; //2、4、8...2048的字体纹理指针SDL_Color fgColor[3] = {{25, 160, 0, 255}, {85, 200, 200, 255}, {55, 0, 100, 255}}; //绿、天蓝、紫

而字体纹理是由字体表面得来的,于是接下来就通过构建字体表面进而构建出纹理来:

Chess2048::Chess2048(SDL_Renderer * render, TTF_Font *font): render(render), font(font) {//在随机的位置随机生成2个数2或4//...//创建字体表面,保存字体纹理for (int i = 0; i < 11; i++) {string s = to_string(((int)pow(2, i+1))); //2、4、8...2048SDL_Surface * textSurface = TTF_RenderUTF8_Solid(font, s.c_str(), this->fgColor[i % 3]);this->textSize[i].w = textSurface->w; this->textSize[i].h = textSurface->h;this->texture[i] = SDL_CreateTextureFromSurface(render, textSurface);SDL_FreeSurface(textSurface); //释放字体表面}
}
2.2.3 绘制文字

同矩形绘制一样,在show成员函数中实现:

void Chess2048::show(int interval) {//设置背景色白色SDL_SetRenderDrawColor(render, 255, 255, 255, 255); //清空渲染器SDL_RenderClear(render);for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {// cout << this->gameBoard[i*4+j] << " ";//命令行显示//绘制格子部分//...//绘制不为0的数字int num = this->gameBoard[i*4+j];if (num) {int index = (int)log2(num) - 1;SDL_Rect textRect = {j*DIM+DIM/2 - this->textSize[index].w/2, i*DIM+DIM/2 - this->textSize[index].h/2, this->textSize[index].w, this->textSize[index].h}; //字 SDL_RenderCopy(this->render, this->texture[index], nullptr, &textRect); //显示}}// cout << endl; //命令行显示}SDL_RenderPresent(this->render);SDL_Delay(interval);
}

(三) 随机生成2个数字(2或4)

知识点:伪随机数生成。

  1. 主要是在构造函数是实现的,能产生2到3个随机的2或4:
Chess2048::Chess2048(SDL_Renderer * render, TTF_Font *font): render(render), font(font) {//在随机的位置随机生成2个数2或4srand(time(NULL));this->gameBoard[rand() % 16] = (rand() % 2 + 1) * 2;this->gameBoard[rand() % 16] = (rand() % 2 + 1) * 2;//随机生成第3个数if (rand() % 2) {this->gameBoard[rand() % 16] = (rand() % 2 + 1) * 2;}//构造字体部分//...
}

这里需要提醒的是srand(time(NULL));必须要,不然光靠rand()函数是不能生成伪随机数的。

  1. 其次在移动棋盘过程中也会随机出现不多于2个的数字2或4:
//如果发生了移动则在空余位置生成随机的2或4
if (moved) {int pos = rand() % 16;if (!this->gameBoard[pos]) this->gameBoard[pos] = (rand() % 2 + 1) * 2;pos = rand() % 16;if (!this->gameBoard[pos]) this->gameBoard[pos] = (rand() % 2 + 1) * 2;
}

(四) 捕获用户方向键操作

在main函数中实现:

Chess2048 chess = Chess2048(render, ttf);
while(true) {if (chess.win()) {cout << "You Win!" << endl;break;} else if (chess.full()) {cout << "You Lose!" << endl;break;}//捕获事件SDL_Event event;SDL_PollEvent(&event); //等待事件
// cout << event.type << " " << flush;if (event.type == SDL_QUIT) { // 退出break;} else if (event.type == SDL_KEYDOWN) { // 按键//防抖while(true) {SDL_PollEvent(&event); //等待事件if ((event.type == SDL_KEYUP)) break;}switch (event.key.keysym.sym) { //检测按了哪个方向键case SDLK_UP:chess.up();break;case SDLK_DOWN:chess.down();break;case SDLK_LEFT:chess.left();break;case SDLK_RIGHT:chess.right();break;}} //end of ifchess.show(20); //按频率显示
} //退出循环

这时event.type表示SDL事件类型,我捕获的是按键,但按键有按下和抬起2种事件,因此增加了防抖机制,即直到按下后抬起再开始响应。而event.key.keysym.sym可以通过与SDL内置的键码进行比较,就可判断是哪个方向键按下了。

1.合并运算函数

这一部分是重难点,以上移操作为例,使用了变量k作为指示功能:

bool Chess2048::up() {bool moved = false;//所有格子偿试上移for (int j = 0; j < 4; j++) { //列int k = -1; // 标记压缩后不为0的行标末尾if (this->gameBoard[j]) k = 0;for (int i = 1; i < 4; i++) { //行if (!this->gameBoard[i*4+j]) continue;k++;if (k < i) { //直到不为0时往前压缩this->gameBoard[k*4+j] = this->gameBoard[i*4+j];this->gameBoard[i*4+j] = 0;moved = true;}if (k > 0 && this->gameBoard[(k-1)*4+j] == this->gameBoard[k*4+j]) { //与上一行相同则合并this->gameBoard[(k-1)*4+j] *= 2;this->gameBoard[k*4+j] = 0;k--;moved = true;}}}//如果发生了移动则在空余位置生成随机的2或4//...return moved;
}

(五) 更新游戏界面

在(四) 捕获用户方向键操作部分已经提到了,即chess.show(20); //按频率显示

(六) 判断游戏结束

1.判断获胜

bool Chess2048::win() {for (int i = 0; i < 16; i++) {if (this->gameBoard[i] == 2048) return true;}return false;
}

2.判断失败

判断完获胜后,只需判断有没有没到2048就满格的,但需判断是不是假满格,所谓假满格就是相邻格子可以合并的:

//判断是否满格,不能是假满格(即相邻单元格可以合并)
bool Chess2048::full() {// 所有数字全部和左、上比较for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {if (!this->gameBoard[i*4+j]) return false; //如果有0,就不满if (i == 0 && j == 0) continue;if (j > 0 && this->gameBoard[i*4+j] == this->gameBoard[i*4+j-1]) return false; //和左边相等,假满格if (i > 0 && this->gameBoard[i*4+j] == this->gameBoard[(i-1)*4+j]) return false; //和上边相等,假满格}}return true;
}

三、总结

本文教大家使用C++语言实现一个图形化2048小游戏,希望有所收获,如有好的建议欢迎留言,谢谢大家啦!

四、源码下载

2048小游戏(C++ SDL版)

相关文章:

  • 力扣4.寻找两个正序数组的中位数
  • java int 颜色值转换为string 不带透明度
  • JavaScript 核心原理深度解析-不停留于表面的VUE等的使用!
  • LeetCode 152. 乘积最大子数组 - 动态规划解法详解
  • 【CF】Day74——⭐Codeforces Round 885 (Div. 2) ACD (数学场)
  • 2025.6.3学习日记 Nginx 基本概念 配置 指令 文件
  • Nginx配置Ollama 访问api服务
  • 61、ESB详解
  • RTP over TCP 模式
  • Ros2 简单构建项目的流程以及涉及的文件作用
  • 【iOS安全】Macbook更换brew源
  • AI一周事件(2025年5月27日-6月2日)
  • 在图像分析算法部署中应对流行趋势的变化|文献速递-深度学习医疗AI最新文献
  • 【北邮 操作系统】第十二章 文件系统实现
  • Windows应用-音视频捕获
  • MongoTemplate常用api学习
  • 四元素、旋转矩阵与旋转向量
  • 小体积涵盖日常办公等多功能的软件
  • 第三章 3.MAC Address(CCNA)
  • linux的实时性
  • 英文建站/灰色关键词排名代做
  • 网站建设app小程序开发/企业如何注册自己的网站
  • 徐州手机网站建设/百度权重3的网站值多少
  • 高佣联盟做成网站怎么做/google推广公司
  • 房屋装修设计网站/网站内容seo
  • 换友链的网站/哈尔滨网站推广