前言

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

✨效果如下

2023-05-26-14-19-21推箱子


文章目录

    • 前言
    • 一、推箱子思路讲解
    • 二、加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#include#include#includeusing 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;}

四、总结

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