推箱子C语言版
C语言推箱子实战控制台版,简单易懂
文章目录
学了挺久的C语言,没有一个小项目来练练手,是不是感觉挺无聊的,这篇文章将给你带来学习C语言的兴趣。要求C语言学到数组和函数就可以实现了。很简单易懂的代码。控制台版的C语言推箱子。
想直接看源码可以直接点击“整体源代码”获取。想详细地看每一步骤的讲解就耐心看完叭。
开发环境
VS2019,其他的开发环境也是可以的,VC6.0的话可能要修改一下代码。
需要的头文件
#include<stdio.h> //标准输入输出头文件
#include<conio.h> //按键getch()函数的头文件
#include<stdlib.h> //system()函数的头文件
特殊字符测试
//↓☉⊙●〇◎¤★☆■▓「」『』◆◇▲△▼▽◣◥
//◢◣◤ ◥№↑↓→←↘↙Ψ※㊣∑⌒∩【】〖〗
//@ξζω□∮〓※》∏卐√ ╳々♀♂∞①ㄨ≡╬╭╮
//╰╯╱╲ ▂ ▂ ▃ ▄ ▅ ▆ ▇ █ ▂▃▅▆█ ▁▂
这些字符可以直接百度查找,然后使用printf()函数来调用看那些字符可以使用,以下的字符是这个文章推箱子使用到的字符
/*
* 空地: 两个空格 0
墙: ■ 1
人: ♀ 5
箱子: □ 3
目的地: ☆ 4
箱子在目的地上: ★ 7 : 3+4
人在目的地上: ♀ 9 :5+4
*/
并且使用一些特殊的数字表示字符的含义。方便在人物移动的时候应用。
主体逻辑
使用一个死循环,然后依次进行画图,移动人擦图的过程,直到地图中没有箱子就结束程序或者是跳转到下一关,然后等到所有的关卡都通过了就结束程序。
只有一关的主函数代码:
int main()
{
while (1)
{
drawMap(); //画图
keyDown(); //移动人物
system("cls"); //擦图
if (gameOver()) //判断游戏是否结束
break;
}
drawMap(); //把最后的图再画一下
printf("游戏结束...\n");
return 0;
}
明确这些之后就一个个实现对应的函数。
地图
使用二维数组来制定游戏的地图,用不同的数字表示不同的图案。
#define ROW 10
#define COL 10
为了可读性,所有把10行10列的地图用ROW表示行COL表示列。
int Map[ROW][COL] = {
1,1,1,1,4,1,1,1,1,1,
1,4,0,3,4,0,0,0,0,1,
1,0,1,1,4,1,1,0,1,1,
1,4,0,0,3,0,0,0,1,1,
1,1,1,3,5,3,4,0,1,1,
1,0,1,0,0,1,0,1,1,1,
1,0,1,0,0,1,0,1,1,1,
1,0,0,0,0,0,0,3,4,1,
1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1
};
/*
* 空地: 两个空格 0
墙: ■ 1
人: ♀ 5
箱子: □ 3
目的地: ☆ 4
箱子在目的地上: ★ 7 : 3+4
人在目的地上: ♀ 9 :5+4
*/
现在这里的只是测试的环节,就先写一关的游戏,等下觉得不够的话可以再加关卡。
人的坐标
用全局的变量表示人的坐标,当下就通过这个全局变量去确定人的位置。如果学过结构体的可以用结构体来封装,没学过就用全局变量搞定。
int x=0;
int y=0;
各个子函数的实现
画图的函数
void drawMap()
{
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
switch (Map[i][j])
{
case 0: //空地: 两个空格 0
printf(" ");
break;
case 1: //墙: ■ 1
printf("■");
break;
case 3: //箱子: □ 3
printf("□");
break;
case 4: //目的地:☆ 4
printf("☆");
break;
case 5: //人: ♀ 5
printf("♀");
break;
case 7: //箱子在目的地上: ★ 7 : 3+4
printf("★");
break;
case 9: //人在目的地上: ♀ 9 :5+4
printf("♀");
break;
}
}
printf("\n");
}
}
思路:使用循环加选择相互嵌套,遍历二维数组的每一个元素,根据元素的值打印对应的字符。其中空地一定要两个空格。因为两个空格的大小才刚好和其他字符是一样大的。
找人位置的函数
void findHero()
{
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (Map[i][j]==5||Map[i][j]==9)
{
x = i;
y = j;
}
}
}
}
思路:因为在子函数里面是不能返回两个变量的值的,所有人的坐标就要是全局变量,前面也说过了可以是用结构体封装人的坐标,但是标题说是简单易懂,所有也不采用结构体的方式写人的坐标。采用循环遍历二维数组,找到人的位置就把对应的i和j的值赋值给x和y。而人的位置是有两种的。
- 人在空地上:Map[i][j]==5的情况
- 人在目的地上:Map[i][j]==9的情况
按键的函数
void keyDown()
{
int key = _getch();
switch (key)
{
case 72: //上
up();
break;
case 80: //下
down();
break;
case 75: //左
left();
break;
case 77: //右
right();
break;
}
}
封装的也很简单,就是对应按下那个键就调用哪个函数。其中_getch()有下划线是因为VS做了增强版,其他的编译器不用加这个下划线直接使用getch()就行。至于72,75,77,80这个这些数字依次对应的就是键盘上的上,左,右,下键。而上下左右等函数要在keyDown函数前实现或声明。
人物移动的函数
人物都是要移动的,要不然怎么叫推箱子呢。因为二维数组的对称性,所以移动的函数只用写号一个就可以了,其他的就复制粘贴然后改一下代码就可以了。
上
void up()
{
//先找人的位置
findHero();
//人的上面是空地或者人的上面是目的地
if (Map[x - 1][y] == 0||Map[x-1][y]==4)
{
Map[x - 1][y] += 5;
Map[x][y] -= 5;
}
//人的上面是箱子
if (Map[x - 1][y] == 3)
{
//箱子的上面是空地或者箱子的上面是目的地
if (Map[x - 2][y] == 0|| Map[x - 2][y] == 4)
{
Map[x][y] -= 5;
Map[x-1][y] += 2;
Map[x-2][y] += 3;
}
//箱子的上面是墙就不做处理,没必要
}
//人的上面是箱子在目的地上
if (Map[x - 1][y] == 7)
{
//箱子在目的地上的上面是空地或者是新的目的地
if (Map[x - 2][y] == 0 || Map[x - 2][y] == 4)
{
Map[x][y] -= 5;
Map[x - 1][y] += 2;
Map[x - 2][y] += 3;
}
}
}
从代码中其实可以发现有很多地方可以把多个if用&&和||运算符联系起来,但是可读性就有点差。把他们分出来写的话,虽然代码量看起来比较多,但是思路更清晰呀。
思路:移动之前先找人,也就是调用findHero函数,确定人的坐标之后再去移动。然后就分别考虑人的上面是什么情况就做对应的事情。
当前情况下:
//Map[x][y] 表示人的位置
//Map[x-1][y] 表示人上面的位置
//Map[x-2][y] 表示人上面的上面的位置
图解:人的上面是空地或目的地
人原来在的位置是,人走了所以是-5,人后来的位置是,人来了所以+5.这样做就很方便而且很清楚,不管人原来的位置是空地还是人已经在目的地上了,-5的操作得出来的结果和其他的做法是一样的,那这个就比较简单。这也就是为什么一开始的时候采用特殊字符用特定的数字表示就是为了写代码的时候轻松点。运用数学规律比赋值简单。实现完上移动之后其他的向下移动和向左移动等思路也还是和实现向上移动的思路一样,最后发现只是改了人的位置那块的相关代码。直接看代码就可以了。
图解:人的上面是箱子
人的上面是箱子,箱子的上面不管是空地还是目的地,都可以采用+3的方式表示,因为+3就表示箱子来了。我们在drawMap函数中是把3(箱子)和7(3+4箱子在目的地上)的情况都表示了的。现在知道用数字表示的好处了吧。可以省很多麻烦。
下
往下移动的代码思路和往上移动是一样的直接看代码吧:
//Map[x][y] 表示人的位置
//Map[x+1][y] 表示人的下面的位置
//Map[x+2][y] 表示人的下面的下面的位置
void down()
{
//先找人的位置
findHero();
//人的下面是空地或者人的下面是目的地
if (Map[x + 1][y] == 0 || Map[x + 1][y] == 4)
{
Map[x + 1][y] += 5;
Map[x][y] -= 5;
}
//人的下面是箱子
if (Map[x + 1][y] == 3)
{
//箱子的下面是空地或者箱子的下面是目的地
if (Map[x + 2][y] == 0 || Map[x + 2][y] == 4)
{
Map[x][y] -= 5;
Map[x + 1][y] += 2;
Map[x + 2][y] += 3;
}
//箱子的下面是墙就不做处理,没必要
}
//人的下面是箱子在目的地上
if (Map[x + 1][y] == 7)
{
//箱子在目的地上的下面是空地或者是新的目的地
if (Map[x + 2][y]