推箱子大冒险(SDL/C)

news/2025/1/7 21:49:03/

前言

欢迎来到小K的SDL专栏第三小节,本节将为大家带来小项目~C语言SDL版坤坤推箱子详细讲解,这里也为大家上传了源码和图片资源,需要的自取看完以后,希望对你有所帮助

✨效果如下


文章目录

    • 前言
    • 一、推箱子思路讲解
    • 二、加SDL绘图代码
    • 三、完整代码
    • 四、总结


一、推箱子思路讲解

第一步,我们先用枚举把下图中的元素表示出来,分别为空地、墙、目的地、箱子、玩家

enum MyEnum { SPACE, WALL, DEST, BOX, PLAYER };

在这里插入图片描述


第二步我们就需要自己定义一个地图来使用,我这里写的推箱子就三关,所以定义了一个三页的二维数组

//定义一个地图int map[3][ROW][COL] ={{{0,0,0,0,0,0,0,0,0,0},{0,0,0,1,1,1,0,0,0,0},{0,0,0,1,2,1,0,0,0,0},{0,0,0,1,3,1,1,1,1,0},{0,1,1,1,0,3,0,2,1,0},{0,1,2,3,4,0,1,1,1,0},{0,1,1,1,1,3,1,0,0,0},{0,0,0,0,1,2,1,0,0,0},{0,0,0,0,1,1,1,0,0,0},{0,0,0,0,0,0,0,0,0,0}},{{0,0,0,0,0,0,0,0,0,0},{0,0,1,1,0,0,1,1,0,0},{0,1,2,0,1,1,0,2,1,0},{1,0,0,0,0,0,0,0,0,1},{1,0,0,3,4,0,3,0,0,1},{0,1,0,0,0,3,0,0,1,0},{0,0,1,0,3,0,0,1,0,0},{0,0,0,1,2,2,1,0,0,0},{0,0,0,0,1,1,0,0,0,0},{0,0,0,0,0,0,0,0,0,0}},{{1,1,1,1,0,0,1,1,1,1},{1,2,0,1,0,0,1,0,2,1},{1,0,0,1,0,0,1,0,0,1},{1,0,0,1,0,0,1,0,0,1},{1,0,0,1,0,0,1,0,0,1},{1,0,3,1,1,1,1,3,0,1},{1,0,0,3,4,0,3,0,0,1},{1,0,0,0,0,0,0,0,0,1},{1,2,0,0,0,0,0,0,2,1},{1,1,1,1,1,1,1,1,1,1}}};

第三步我们就需要一个推箱子的逻辑思维,比方说,前面是空地要怎么操作,前面是箱子又要怎么办?不要着急,和我先打两个半球~

⭐既然是推箱子,那我们肯定要知道谁推箱子,所以第一小步就是找人,这就好办了,我们直接遍历地图,无非只有两种情况,要么是人站在空地上,由于空地为0,所以只需要判断人,要么是人站在目的地上

//玩家的当前的下标int r = -1;int c = -1;//找到玩家的下标for (int i = 0; i < ROW; i++){for (int k = 0; k < COL; k++){if (map[i][k] == PLAYER || map[i][k] == DEST + PLAYER){r = i;c = k;goto endLoop;}}}
endLoop:;

⭐第二小步就是推箱子的具体逻辑了,这里我们以上为例

玩家前面是箱子玩家前面是空地
箱子的前面是空地或者目的地前面空地+PLAYER,本来人站的地方减去PLAYER
⭐1,把箱子移动到前面 ⭐2,把箱子从原来的位置删掉 ⭐3.把玩家移动到箱子原来的位置⭐4,把玩家从原来的位置删掉
case SDLK_w://如果玩家的前面是空地if (map[r - 1][c] == SPACE || map[r - 1][c] == DEST){map[r - 1][c] += PLAYER;map[r][c] -= PLAYER;}//如果玩家的前面是箱子else if (map[r - 1][c] == BOX || map[r - 1][c] == BOX + DEST){//如果箱子的前面是空地或者目的地if (map[r - 2][c] == SPACE || map[r - 2][c] == DEST){//1,把箱子移动到前面map[r - 2][c] += BOX;//2,把箱子从原来的位置删掉map[r - 1][c] -= BOX;//3,把玩家移动到箱子原来的位置map[r - 1][c] += PLAYER;//4,把玩家从原来的位置删掉map[r][c] -= PLAYER;}}break;

完整的推箱子逻辑代码

//推箱子
void pushBox(int map[][COL], int key)
{//玩家的当前的下标int r = -1;int c = -1;//找到玩家的下标for (int i = 0; i < ROW; i++){for (int k = 0; k < COL; k++){if (map[i][k] == PLAYER || map[i][k] == DEST + PLAYER){r = i;c = k;goto endLoop;}}}
endLoop:;switch (key){case SDLK_w://如果玩家的前面是空地if (map[r - 1][c] == SPACE || map[r - 1][c] == DEST){map[r - 1][c] += PLAYER;map[r][c] -= PLAYER;}//如果玩家的前面是箱子else if (map[r - 1][c] == BOX || map[r - 1][c] == BOX + DEST){//如果箱子的前面是空地或者目的地if (map[r - 2][c] == SPACE || map[r - 2][c] == DEST){//1,把箱子移动到前面map[r - 2][c] += BOX;//2,把箱子从原来的位置删掉map[r - 1][c] -= BOX;//3,把玩家移动到箱子原来的位置map[r - 1][c] += PLAYER;//4,把玩家从原来的位置删掉map[r][c] -= PLAYER;}}break;case SDLK_s:if (map[r + 1][c] == SPACE || map[r + 1][c] == DEST){map[r + 1][c] += PLAYER;map[r][c] -= PLAYER;}else if (map[r + 1][c] == BOX || map[r + 1][c] == BOX + DEST){if (map[r + 2][c] == SPACE || map[r + 2][c] == DEST){map[r + 2][c] += BOX;map[r + 1][c] -= BOX;map[r + 1][c] += PLAYER;map[r][c] -= PLAYER;}}break;case SDLK_a:if (map[r][c - 1] == SPACE || map[r][c - 1] == DEST){map[r][c - 1] += PLAYER;map[r][c] -= PLAYER;}else if (map[r][c - 1] == BOX || map[r][c - 1] == BOX + DEST){if (map[r][c - 2] == SPACE || map[r][c - 2] == DEST){map[r][c - 2] += BOX;map[r][c - 1] -= BOX;map[r][c - 1] += PLAYER;map[r][c] -= PLAYER;}}break;case SDLK_d:if (map[r][c + 1] == SPACE || map[r][c + 1] == DEST){map[r][c + 1] += PLAYER;map[r][c] -= PLAYER;}else if (map[r][c + 1] == BOX || map[r][c + 1] == BOX + DEST){if (map[r][c + 2] == SPACE || map[r][c + 2] == DEST){map[r][c + 2] += BOX;map[r][c + 1] -= BOX;map[r][c + 1] += PLAYER;map[r][c] -= PLAYER;}}break;}}

第四步也是最后一步,我们还需要判断一下过关没有,这个好判断,我们直接遍历整个地图,看看还有没有箱子

//判断是否过关
bool passLevel(int map[][COL])
{for (int i = 0; i < ROW; i++){for (int k = 0; k < COL; k++){if (map[i][k] == BOX){return false;}}}return true;
}

二、加SDL绘图代码

第一步我们先搭建一下我们的绘图框架,具体的可以看我的SDL专栏第二小节

int main(int argc,char* argv[])
{//初始化if (SDL_Init(SDL_INIT_VIDEO) != 0){SDL_Log("Init failed%s\n", SDL_GetError());return -1;}//创建窗口SDL_Window* window = SDL_CreateWindow("pushBox", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 640, SDL_WINDOW_SHOWN);if (!window){SDL_Log("create window failed!%s\n", SDL_GetError());return -1;}//创建渲染器SDL_Renderer* render = SDL_CreateRenderer(window, -1, 0);if (!render){SDL_Log("create Renderer failed!%s\n", SDL_GetError());return -1;}/*****************************///接下来的操作///*****************************///销毁窗口SDL_DestroyWindow(window);//销毁渲染器SDL_DestroyRenderer(render);//清理并退出SDL库SDL_Quit();return 0;
}

第二步加载图片,可以看到这里准备的图片编号刚刚好就是0~6,一会在绘图上有大用,这里也有大用,这里我们直接创建一个数组,然后用SDL_snprintf在一个for循环中直接把七张图片显示加载出来
在这里插入图片描述

//加载图片SDL_Texture* tex[7];char fillname[50] = { 0 };for (int i = 0; i < 7; i++){SDL_snprintf(fillname, 50, "./assets/images/%d.bmp", i);tex[i] = loadTexture(render, fillname);}SDL_Texture* loadTexture(SDL_Renderer* ren, const char* fillname)
{SDL_Surface* sfc = SDL_LoadBMP(fillname);if (!sfc){SDL_Log("loadBMP failed!%s\n", SDL_GetError());return NULL;}SDL_Texture* tex = SDL_CreateTextureFromSurface(ren, sfc);if (!tex){SDL_Log("Texture failed!%s\n", SDL_GetError());SDL_FreeSurface(sfc);return NULL;}SDL_FreeSurface(sfc);return tex;
}

最后一步绘制图案,注意这里有一个小坑,二维数组横向是Y,纵向是X,而绘图窗口横向是X,纵向是Y,所以绘图的时候要刚好反一下,如下图
在这里插入图片描述

//绘制界面
void drawMap(SDL_Renderer* render, int map[][COL], SDL_Texture** texs)
{for (int r = 0; r < ROW; r++){for (int c = 0; c < COL; c++){SDL_Rect dstRect = { c * 64 ,r * 64,64,64 };SDL_RenderCopy(render, texs[map[r][c]], NULL, &dstRect);}}
}

三、完整代码

#include<conio.h>
#include<SDL.h>
#include<iostream>
#include<cstdio>
using namespace std;#define ROW 10
#define COL 10enum MyEnum { SPACE, WALL, DEST, BOX, PLAYER };//提前声明
void pushBox(int map[][COL], int key);
bool passLevel(int map[][COL]);
SDL_Texture* loadTexture(SDL_Renderer* ren, const char* fillname);
void drawMap(SDL_Renderer* render, int map[][COL], SDL_Texture** texs);int main(int argc,char* argv[])
{//初始化if (SDL_Init(SDL_INIT_VIDEO) != 0){SDL_Log("Init failed%s\n", SDL_GetError());return -1;}//创建窗口SDL_Window* window = SDL_CreateWindow("pushBox", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 640, SDL_WINDOW_SHOWN);if (!window){SDL_Log("create window failed!%s\n", SDL_GetError());return -1;}//创建渲染器SDL_Renderer* render = SDL_CreateRenderer(window, -1, 0);if (!render){SDL_Log("create Renderer failed!%s\n", SDL_GetError());return -1;}//加载图片SDL_Texture* tex[7];char fillname[50] = { 0 };for (int i = 0; i < 7; i++){SDL_snprintf(fillname, 50, "./assets/images/%d.bmp", i);tex[i] = loadTexture(render, fillname);}//定义一个地图int map[3][ROW][COL] ={{{0,0,0,0,0,0,0,0,0,0},{0,0,0,1,1,1,0,0,0,0},{0,0,0,1,2,1,0,0,0,0},{0,0,0,1,3,1,1,1,1,0},{0,1,1,1,0,3,0,2,1,0},{0,1,2,3,4,0,1,1,1,0},{0,1,1,1,1,3,1,0,0,0},{0,0,0,0,1,2,1,0,0,0},{0,0,0,0,1,1,1,0,0,0},{0,0,0,0,0,0,0,0,0,0}},{{0,0,0,0,0,0,0,0,0,0},{0,0,1,1,0,0,1,1,0,0},{0,1,2,0,1,1,0,2,1,0},{1,0,0,0,0,0,0,0,0,1},{1,0,0,3,4,0,3,0,0,1},{0,1,0,0,0,3,0,0,1,0},{0,0,1,0,3,0,0,1,0,0},{0,0,0,1,2,2,1,0,0,0},{0,0,0,0,1,1,0,0,0,0},{0,0,0,0,0,0,0,0,0,0}},{{1,1,1,1,0,0,1,1,1,1},{1,2,0,1,0,0,1,0,2,1},{1,0,0,1,0,0,1,0,0,1},{1,0,0,1,0,0,1,0,0,1},{1,0,0,1,0,0,1,0,0,1},{1,0,3,1,1,1,1,3,0,1},{1,0,0,3,4,0,3,0,0,1},{1,0,0,0,0,0,0,0,0,1},{1,2,0,0,0,0,0,0,2,1},{1,1,1,1,1,1,1,1,1,1}}};//当前所在关卡int level = 0;SDL_bool isDown = SDL_FALSE;while (!isDown){drawMap(render, map[level], tex);SDL_RenderPresent(render);if (passLevel(map[level])){level++;if (level >= 3){std::cout << "game over,你通过了所有关卡~" << endl;level = 0;break;}std::cout << "恭喜你,通过了第" << level - 1 << "关!" << endl;}SDL_Event ev = { 0 };if (SDL_PollEvent(&ev)){if (SDL_QUIT == ev.type){isDown = SDL_TRUE;}else if (ev.type == SDL_KEYDOWN){pushBox(map[level], ev.key.keysym.sym);}}}//销毁窗口SDL_DestroyWindow(window);//销毁渲染器SDL_DestroyRenderer(render);//清理并退出SDL库SDL_Quit();return 0;
}//绘制界面
void drawMap(SDL_Renderer* render, int map[][COL], SDL_Texture** texs)
{for (int r = 0; r < ROW; r++){for (int c = 0; c < COL; c++){SDL_Rect dstRect = { c * 64 ,r * 64,64,64 };SDL_RenderCopy(render, texs[map[r][c]], NULL, &dstRect);}}
}//推箱子
void pushBox(int map[][COL], int key)
{//玩家的当前的下标int r = -1;int c = -1;//找到玩家的下标for (int i = 0; i < ROW; i++){for (int k = 0; k < COL; k++){if (map[i][k] == PLAYER || map[i][k] == DEST + PLAYER){r = i;c = k;goto endLoop;}}}
endLoop:;switch (key){case SDLK_w://如果玩家的前面是空地if (map[r - 1][c] == SPACE || map[r - 1][c] == DEST){map[r - 1][c] += PLAYER;map[r][c] -= PLAYER;}//如果玩家的前面是箱子else if (map[r - 1][c] == BOX || map[r - 1][c] == BOX + DEST){//如果箱子的前面是空地或者目的地if (map[r - 2][c] == SPACE || map[r - 2][c] == DEST){//1,把箱子移动到前面map[r - 2][c] += BOX;//2,把箱子从原来的位置删掉map[r - 1][c] -= BOX;//3,把玩家移动到箱子原来的位置map[r - 1][c] += PLAYER;//4,把玩家从原来的位置删掉map[r][c] -= PLAYER;}}break;case SDLK_s:if (map[r + 1][c] == SPACE || map[r + 1][c] == DEST){map[r + 1][c] += PLAYER;map[r][c] -= PLAYER;}else if (map[r + 1][c] == BOX || map[r + 1][c] == BOX + DEST){if (map[r + 2][c] == SPACE || map[r + 2][c] == DEST){map[r + 2][c] += BOX;map[r + 1][c] -= BOX;map[r + 1][c] += PLAYER;map[r][c] -= PLAYER;}}break;case SDLK_a:if (map[r][c - 1] == SPACE || map[r][c - 1] == DEST){map[r][c - 1] += PLAYER;map[r][c] -= PLAYER;}else if (map[r][c - 1] == BOX || map[r][c - 1] == BOX + DEST){if (map[r][c - 2] == SPACE || map[r][c - 2] == DEST){map[r][c - 2] += BOX;map[r][c - 1] -= BOX;map[r][c - 1] += PLAYER;map[r][c] -= PLAYER;}}break;case SDLK_d:if (map[r][c + 1] == SPACE || map[r][c + 1] == DEST){map[r][c + 1] += PLAYER;map[r][c] -= PLAYER;}else if (map[r][c + 1] == BOX || map[r][c + 1] == BOX + DEST){if (map[r][c + 2] == SPACE || map[r][c + 2] == DEST){map[r][c + 2] += BOX;map[r][c + 1] -= BOX;map[r][c + 1] += PLAYER;map[r][c] -= PLAYER;}}break;}}//判断是否过关
bool passLevel(int map[][COL])
{for (int i = 0; i < ROW; i++){for (int k = 0; k < COL; k++){if (map[i][k] == BOX){return false;}}}return true;
}//加载图片
SDL_Texture* loadTexture(SDL_Renderer* ren, const char* fillname)
{SDL_Surface* sfc = SDL_LoadBMP(fillname);if (!sfc){SDL_Log("loadBMP failed!%s\n", SDL_GetError());return NULL;}SDL_Texture* tex = SDL_CreateTextureFromSurface(ren, sfc);if (!tex){SDL_Log("Texture failed!%s\n", SDL_GetError());SDL_FreeSurface(sfc);return NULL;}SDL_FreeSurface(sfc);return tex;
}

四、总结

本节带来的推箱子小游戏不仅可以让你对数组等语法更加熟练,也会让你的图形库知识更上一层楼,本节就到这里啦~期待下一节和大家的相遇🌞


http://www.ppmy.cn/news/92728.html

相关文章

从小白到大神之路之学习运维第28天

第二阶段基础 时 间&#xff1a;2023年5月24日 参加人&#xff1a;全班人员 内 容&#xff1a; ISCSI服务 目录 实验构建iscsi服务 实现步骤&#xff1a;以下实验为例 前提配置&#xff1a; 服务器端&#xff1a; 客户端&#xff1a; 注意事项&#xff1a; 实验构…

ggerganov/llama.cpp 编译

ggerganov/llama.cpp - 编译出main可执行程序依赖以下的源代码文件 如果想在 Windows 系统编译出 llama.cpp 项目&#xff08;这个是github上的仓库&#xff0c; ggerganov/llama.cpp &#xff09;&#xff0c;需要在Visual Studio上添加项目内的若干个源文件。这篇简陋的笔记…

transformers 的使用

一.配置环境 在 抱抱脸 网址 上发布了很多已经训练好的模型&#xff0c;基本上大量的NLP模型都在&#xff0c;一开始是发布 transformers 的开源库&#xff0c;但后来连接了开发者和使用者。 https://huggingface.co/ 在安装 transformers 前&#xff0c;需要先安装Flax&…

线程和进程区别

什么是线程和进程? 进程 一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间&#xff0c;一个进程可以有多个线程&#xff0c;比如在Windows系统中&#xff0c;一个运行的xx.exe就是一个进程。 线程进程中的一个执行任务&#xff08;控制单元&#xff09;&#…

百度松果菁英班--oj赛(第五次)

文章目录 百度松果菁英班--oj赛&#xff08;第五次&#xff09;一、附庸的附庸二、采蜜三、暧昧团四、上楼梯五、上楼梯2六、大厨小码哥七、纸带八、围栏木桩九、最长字段和十、旅费 百度松果菁英班–oj赛&#xff08;第五次&#xff09; 一、附庸的附庸 **题目&#xff1a;*…

TexSAW|2023|Cryptography&Misc|WP

Cryptography&#xff5c;Crack the crime 用 nc 连上后&#xff0c;直接得到第一题 是一个简单的base64加密&#xff0c;解密如下&#xff1a; Meet in dubai on Tuesday 填入之后可获得第二题 猜测是古典加密&#xff0c;随后经过N次尝试后发现是rot13加密&#xff0c;解密…

Google 出的C++轻量级日志库_GLog_了解_使用

文章目录 1、Google Logging&#xff08;glog&#xff09;是一个C轻量级、稳定、开源的日志系统日志库,是一个实现应用程序级日志记录的C98库&#xff0c;支持以下功能&#xff1a;2、GLog日志级别&#xff1a;3、GLog分类&#xff1a;1、日志分类&#xff1a;2、条件型LOG 4、…

GET和POST区别

GET和POST区别 GET&#xff1a;从服务器上获取数据&#xff0c;也就是所谓的查&#xff0c;仅仅是获取服务器资源&#xff0c;不进行修改。 POST&#xff1a;向服务器提交数据&#xff0c;这就涉及到了数据的更新&#xff0c;也就是更改服务器的数据。 PUT&#xff1a;英…