推箱子想必是很多人童年时期的经典游戏,我们依旧能记得抱个老人机娱乐的场景,今天同小白博主一起用c/c++实现一个简易版推箱子小游戏吧。
在正式写代码前,我们必须要考虑好编写这个游戏整体的思路,例如我们怎样建立一个地图,怎样让人上下左右行走,什么时候才能走,什么时候不能走,怎样表示游戏中的人物,空地,墙…等等等等,为了不让程序显的杂乱无章,提高代码易读性,我们最好编写一些函数来实现游戏中的各个功能。那么我们一一来分析。
假如地图是这样的:
我们可以用一个二维数组来存储这个地图。
int map[8][8]={
{1,1,1,1,1,1,1,1},//0 空地
{1,0,0,0,1,0,0,1},//1 墙
{1,0,1,0,1,4,3,1},//3 目的地
{1,0,0,0,0,4,3,1},//4 箱子
{1,0,1,0,1,4,3,1},//5 人
{1,0,0,0,1,0,0,1},//7 箱子和目的地重合
{1,1,1,1,1,5,0,1},//8 人和目的地
{0,0,0,0,1,1,1,1}};
可以看到图是一个8*8的大小,我们分别用0.1.3.4.5.7.8代表图中的各个元素,在这里我们留了一个伏笔,7刚好等于3+4刚好是目的地+箱子,8刚好是5+3刚好是+人和目的地,一会我们在进行人物移动判断的时候可以直接用相对位置上的加减法就可以实现了。
但是这样看地图不太容易识别,我们在用两个for循环遍历数组加一个switch语句来分别输出相应的图片
for(int i=0;i<8;i++)
{
for(int j=0;j<8;j++)
{
switch(map[i][j])
{
case 0:printf(" ");break;
case 1:printf("■");break;
case 3:printf("☆");break;
case 4:printf("□");break;
case 5:printf("♀");break;
case 7:printf("★");break;
case 8:printf("♀");break;
}
}
cout<<endl;
}
效果给大家看一下
这样是不是图片的大致形状就有了,如果大家想玩不同的关卡只需要改变数组的大小或者数组中的数字即可!
接下来我们如何控制人物的移动呢?首先我们要找到当前人物的位置,我们依旧只需遍历一遍数组即可找到,并将其下标存储下来,便于接下来的移动判断。
int r,c;//人的下标
int flag=0;
for(r=0;r<8;r++)
{
for(c=0;c<8;c++)
{if(map[r][c]==5||map[r][c]==8)
{
flag=1;
break;
}
}
if(flag)
break;
}
在这里我们用flag的用意是如果找到人了,就可以退出循环了,不必进行不必要的操作,以免浪费资源。
这里我们用getch()来接受键盘上的方向控制
找到人后,我们以向上作为例子进行判断,我们只需要对可以移动时进行编写代码操作,不能移动时我们不必做任何事。有这么几种情况是可以移动的:
1.上面是空地
2.上面是目的地
3.上面是箱子,并且箱子上面是目的地或空地
4.上面是箱子+目的地,并且箱子+目的地上面是目的地或空地
我们用if语句来判断
if(map[r-1][c]==0)//1.对应上面是空地
{map[r-1][c]=5;//在上面生成一个人
map[r][c]=0;}//原来的人置换成空地
else if(map[r-1][c]==3)//2.对应上面是目的地
{map[r-1][c]=8;//目的地变成目的地+人
map[r][c]=0;}//原来的人置换为空地
else if(map[r-1][c]==4)//3.对应上面是箱子
{ if(map[r-2][c]==3)//箱子上面是目的地
{
map[r-2][c]=7;//箱子上面变成箱子+目的地
map[r-1][c]=5;//箱子处替换成人
map[r][c]=0;//人替换成空地
}
if(map[r-2][c]==0)//箱子上面是是空地
{ map[r-2][c]=3;//箱子上面变成箱子
map[r-1][c]=5;//箱子处替换成人
map[r][c]=0;//人替换成空地
}
}
else if(map[r-1][c]==7)//4.对应上面是箱子+目的地
...//我们可以同3.情况一样修改其中的数字即可
这样虽然可以把所有向上的情况总结出来,但是过于繁琐。
还记得我们在开始列数字时留下的伏笔吗?
现在就可以用到了,所以我们直接可以给原数+5表示人来了,给原数-5表示人走了,给原数+4表示多了个箱子,给原数-4表示走了个箱子,所以给原式+5-4表示人来了并且箱子走了。并且不论上面是目的地还是空地,都适用这样的结论,因为我们有意的设置了这样的值。这样既可大大减少代码量,上修改后的代码!
if(map[r-1][c]==0||map[r-1][c]==3)//人的前面是空地或目的地
{ map[r-1][c]+=5; //人来+5
map[r][c]-=5;//人走-5
}
else if(map[r-1][c]==4||map[r-1][c]==7)//人前面是箱子或箱子加目的地
{
if(map[r-2][c]==0||map[r-2][c]==3)//人前面的前面是空地或目的地
{
map[r-2][c]+=4;//箱子来了
map[r-1][c]+=1;//人来箱子走
map[r][c]-=5;//人走了
}
}
是不是很神奇,只用了3个if语句就将上述情况全部涵盖了!!!
有了一个方向,其余方向也是信手拈来,例如:
向下只需要将数组中的r-1变成r+1即可。
向左只需要将r复原,c修改成c-1。
向右只需要将c修改成c+1。
其余代码全部不变。
我们来看一下整体效果!
#include<iostream>
#include<stdlib.h>
#include<conio.h>
using namespace std;
int map[8][8]={
{1,1,1,1,1,1,1,1},//0 空地
{1,0,0,0,1,0,0,1},//1 墙
{1,0,1,0,1,4,3,1},//3 目的地
{1,0,0,0,0,4,3,1},//4 箱子
{1,0,1,0,1,4,3,1},//5 人
{1,0,0,0,1,0,0,1},//7 箱子和目的地重合
{1,1,1,1,1,5,0,1},//8 人和目的地
{0,0,0,0,1,1,1,1}};
void GamePaint()
{
//输出
for(int i=0;i<8;i++)
{
for(int j=0;j<8;j++)
{
switch(map[i][j])
{
case 0:printf(" ");break;
case 1:printf("■");break;
case 3:printf("☆");break;
case 4:printf("□");break;
case 5:printf("♀");break;
case 7:printf("★");break;
case 8:printf("♀");break;
}
}
cout<<endl;
}
}
void GamePlay()
{
int r,c;//人的下标
int flag=0;
for(r=0;r<8;r++)
{
for(c=0;c<8;c++)
{if(map[r][c]==5||map[r][c]==8)
{
flag=1;
break;
}
}
if(flag)
break;
}
cout<<"人的下标:"<<r<<" "<<c;
char key;
key=getch();
switch(key)
{
case 'w':
if(map[r-1][c]==0||map[r-1][c]==3)//人的前面是空地或目的地
{ map[r-1][c]+=5; //人来+5
map[r][c]-=5;//人走-5
}
else if(map[r-1][c]==4||map[r-1][c]==7)//人前面是箱子或箱子加目的地
{
if(map[r-2][c]==0||map[r-2][c]==3)//人前面的前面是空地或目的地
{
map[r-2][c]+=4;
map[r-1][c]+=1;
map[r][c]-=5;
}
}
break;
case 's':
if(map[r+1][c]==0||map[r+1][c]==3)//人的后面是空地或目的地
{ map[r+1][c]+=5; //人来+5
map[r][c]-=5;//人走-5
}
else if(map[r+1][c]==4||map[r+1][c]==7)//人后面是箱子或箱子加目的地
{
if(map[r+2][c]==0||map[r+2][c]==3)//人后面的前面是空地或目的地
{
map[r+2][c]+=4;
map[r+1][c]+=1;
map[r][c]-=5;
}
}
break;
case 'a':
if(map[r][c-1]==0||map[r][c-1]==3)//人的左面是空地或目的地
{ map[r][c-1]+=5; //人来+5
map[r][c]-=5;//人走-5
}
else if(map[r][c-1]==4||map[r][c-1]==7)//人左面是箱子或箱子加目的地
{
if(map[r][c-2]==0||map[r][c-2]==3)//人左面的左面是空地或目的地
{
map[r][c-2]+=4;
map[r][c-1]+=1;
map[r][c]-=5;
}
}
break;
case 'd':
if(map[r][c+1]==0||map[r][c+1]==3)//人的右面是空地或目的地
{ map[r][c+1]+=5; //人来+5
map[r][c]-=5;//人走-5
}
else if(map[r][c+1]==4||map[r][c+1]==7)//人右面是箱子或箱子加目的地
{
if(map[r][c+2]==0||map[r][c+2]==3)//人右面的右面是空地或目的地
{
map[r][c+2]+=4;
map[r][c+1]+=1;
map[r][c]-=5;
}
}
break;
}
}
int main()
{ while(1)//让下面语句不断运行
{system("cls");//清屏函数
GamePaint();
GamePlay();
}
return 0;
}
该代码待改进处:
1.缺少成功后的输出显示及停止功能。
2.缺少提示游戏操作功能
3.缺少“悔棋”功能
前两个不难实现,例如我们可以用判断原数组中是否还存在独立的箱子而非箱子加目的地来判断是否成功,或者判断三个目的地处是否全部变成了目的地+箱子。
而游戏操作提示可以在GamePlay()直接输出即可,第三个功能有兴趣的小伙伴可以试试呦!!!
试着玩一下:
只要你们像小白博主一样走的狂野一点,走到这样也是相当容易的~