前言
欢迎来到小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 10
enum 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;
}
四、总结
本节带来的推箱子小游戏不仅可以让你对数组等语法更加熟练,也会让你的图形库知识更上一层楼,本节就到这里啦~期待下一节和大家的相遇🌞