基于Linux系统的C语言多关卡推箱子设计
目录
前言
经典推箱子是一个来自古老的游戏,目的是训练人们的逻辑思维能力,巧妙的利用有限的空间和通道,合理的安排上下左右移动的次序和位置,箱子只可以推,不可以拉,而且一次只能推动一个,胜利的条件是把所有的箱子都推到目的地,从而完成游戏的要求。
现在市场上的推箱子游戏各种各样,有基于Windows下C语言编写的,有基于JAVA和Python设计的,而基于Linux环境下设计的推箱子游戏非常少,因为一些头文件不兼容和人们对Windows操作系统的依赖性,加上Linux操作系统一切都是文件,所有的操作基本上都是在Terminal里面进行操作的,而且Linux下的可视化界面相对没有Windows下更加美观,
而且软件的安装没有Windows下更加便捷,所有的操作都是通过命令去完成实现的,所以设计出一款基于Linux操作系统下的推箱子小游戏也是非常不错的,而且利用Linux下标准IO的操作使得游戏更加的充满趣味性和可玩性,更好的体验该环境下的特色,从而改变人们对于Linux的一些看法,认识到Linux系统下设计的游戏和其他环境下一样,有着显著的优势和特点。
以下是本篇文章正文内容,下面案例仅供参考
一、设计方案
推箱子游戏在开始后,可以通过按Q键,此时调用程序退出函数,结束游戏,也可以按其他键进行游戏初始化,此时进入游戏第一关。
第一关如果挑战失败,此时调用程序退出函数结束游戏,如果第一关挑战成功,显示本关完成挑战,显示所使用的游戏步数。
此时给玩家一个交互的选择,按Q键结束游戏,按回车键进入第二关,依次执行相关的操作。
直到第五关挑战成功,此时显示本关所用的步数,然后退出游戏,游戏结束。
二、功能设计
2.1功能简述
由键盘中的w,s,a,d四个字母分别代表上下左右来控制人的运动;
当本关通过时,可以选择回车进行下一关,也可以选择q键直接退出游戏。
当本关完成后,会显示完成本关所使用的步数,而且每一关都比上一关增加难度,要完 成的推箱子数不同,当5关全部完成后退出游戏,游戏结束。
当箱子碰到墙壁而无法推动时,会进入游戏已经结束,退出游戏。
其他几个方位键操作思想类似,因代码较长,仅以向上方位键展示
代码示例:
switch (ch)
{
case 'w':
case 'W':
//下一个地方等于空地或者是目的 能走
if(7 == map[cas][i-1][j]||3 == map[cas][i-1][j])
{
return;
}
if (map[cas][i - 1][j] == 0 || map[cas][i - 1][j] == 3)
{
//走的实质是交换连个位置的值
//原来的地方(map[i-1][j])人(5)走了
map[cas][i][j] -= 5;
//新的地方(map[i][j])人(5)来了
map[cas][i-1][j] += 5;
step++;
}
//相邻的地方
if (map[cas][i - 1][j] == 4 || map[cas][i - 1][j] == 7)
{
//相邻的地方的相邻
if (map[cas][i - 2][j] == 0 || map[cas][i - 2][j] == 3)
{ //怎么走,三步
//原来的地方
map[cas][i][j] -= 5;
//相邻
map[cas][i - 1][j] += 1;
//隔壁的隔壁
map[cas][i-2][j] += 4;
step++;
}
}
2.2流的类型和操作
标准I/O中流的缓冲类型有3种,分别是全缓冲,行缓冲,无缓冲。
全缓冲。在这种情况下,当填满标准I/O缓冲区后才进行实际的I/O操作。对于存放在磁盘上的普通文件,用标准I/O打开时默认是全缓冲的。当缓冲区已满或执行flush操作时才会进行磁盘操作。
行缓冲。当在输入和输出中遇到换行符时执行I/O操作。
无缓冲。不对I/O操作进行缓冲,即在对流的读写时会立刻操作实际的文件。标准出错流是不带缓冲的。
流的开始代码示例:
void start_soko(void)
{
FILE* frp = fopen("soko.bin","w+");
if(NULL == frp)
{
printf("数据加载错误!\n");
return;
}
fread(map,1,490,frp);
fclose(frp);
}
流的结束代码示例:
void exit_soko(void)
{
FILE* fwp = fopen("soko.bin","w+");
printf("游戏结束,退出游戏");
if(NULL == fwp)
{
printf("数据保存错误!\n");
}
fwrite(map,1,490,fwp);
fclose(fwp);
exit(0);
}
2.3标准I/O
参数 | 功能简述 |
---|---|
r或rb | 打开存在的只读文件 |
r+或r+b | 打开存在的可读写文件 |
w或wb | 打开只写文件,若文件不存在创建文件,文件存在则擦写以前的内容。 |
w+或w+b | 打开可读写文件,若文件不存在创建文件,文件存在则擦写以前的内容。 |
a或ab | 附加的方式打开只写文件,若文件不存在创建文件,文件存在保留原先内容,数据附加在文件尾。 |
a+或a+b | 附加的方式打开可读写文件,若文件不存在创建文件,文件存在保留原先内容,数据附加在文件尾。 |
2.4流的使用
Fopen函数常用于使用标准I/O打开文件,如果打开文件成功则指向FILE的指针,如果打开文件失败则返回NULL。
Fclose函数常用于使用标准I/O关闭文件,该函数将流的缓冲区内的数据全部写入文件中,并释放相关资源。如果关闭文件成功则返回0,如果关闭文件失败则返回错误。
Fread函数是当文件流被打开之后,可对文件流按照指定单位的大小进行读操作。
Fwrite函数是文件流被打开后,可对文件流按照指定的单位大小进行写操作。
2.5两个主要问题
设计中的两个主要解决的问题是:怎样生成地图、怎样实现人或人和箱子的移动。
这也是本次设计的核心和难点,只有解决了这两个问题,推箱子的基本功能才能实现。
然后整个操作都是在地图绘制的基础上进行的,解决了这些基本的问题,才能在此基础上设计更多新颖的附加功能,比如增加游戏的关卡数,计算完成游戏所使用的步数等。
2.6地图的生成
设计中用三维整形数组中的数字元素表示不同的物体,比如0表示空地,1表示墙,3表示目的地,4表示箱子,5表示人,7表示箱子推到目的地。
然后用一个switch语句给每个数字赋予不同的符号,再输出三维数组,生成地图。
2.7人或人和箱子的移动
设计中要想使人移动,首先要找到人的起始位置,然后才能开始移动。人向四个方向移动的原理是相同的,可以先研究一个方向的移动,找到人的位置后,以人向上走为例,可以通过数组下标表示。
首先要判断人的上面是否为空地,因为向上进行移动,是空地则可以进行移动,移动后判断并改变人员原位置的数值元素和空地原位置的数值元素;否则不移动。
如果人的上面是目的地,人也是可以移动的,同样判断人原位置数值元素,并改变人员位置的数值元素和前一位置的数值元素。
如果人前面是空地上的箱子。如果箱子前面是空地,则可以进行移动,改变箱子前位置的数值元素,还是判断原位置的数值元素,然后改之;如果箱子前为目的地,则一样移动和改变数值元素,使得箱子推到目的地。
如果人前面是箱子已经推到目的地,则不可推动已经到达目的地的箱子,如果人的另一面是空地,则可以进行移动,执行上面的一些操作。
每一次移动,在完成本关后,都会进行初始化,然后再一次显示相应关卡的地图,这样便可以实现人或人和箱子的移动。
三、模块设计与使用
本设计包括5个模块,分别是初始化模块、图画模块、箱子移动模块、小人移动模块和功能控制模块。
3.1初始化模块
该模块包括屏幕初始化和游戏每一关的初始化。屏幕初始化主要用于输出游戏的操作提示,游戏的每一关的初始化是构建每一关的关卡和箱子数。
初始化部分代码示例:
system("clear");//清屏
if(1==lose())
{
system("clear");
printf("游戏失败!\n");
exit_soko();return 0;
}
3.2图画模块
该模块主要用于被其他模块调用,用于画墙、画箱子、画目的地、画小人。
其中墙用“#”表示,箱子用“$”表示,目的地用“o”表示,小人用“@”表示。
地图模块(思想)的代码示例:
int map[填空][填空][填空] =
/*三维数组里第一个表示关卡数,后面两个二表示行和列*/
{
//0:空 1:#:墙
//3:0 4:$ //目的地和箱子
//5:@ //人
//7:m //目的(3)和箱子(4)在一起
//8:@ //人(5)和目的(3)在一起
/*二维数组示例*/
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,
1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,
1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0,0,0,0,0,1,1,1,1,
1,0,0,1,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,0,0,0,0,0,0,0,0,0,0,1,4,0,0,0,0,1,0,0,0,0,0,1,1,
1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,3,0,0,0,0,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,4,0,0,0,1,0,0,0,1,1,
1,1,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,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,1,1,1,
1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,
1,0,0,0,0,0,0,0,0,0,0,0,0,3,0,4,0,5,0,0,0,0,0,0,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,}
};
int cas = 0; //为0表示第一关
//记录每一关的箱子数 或者是项目和目的在一起的总数
int boxSum[] = {第一关目标,第二关目标,......};
3.3箱子移动模块
该模块用于实现对箱子的上下左右移动,包括目的地之间、空地之间、目的地与空地之间箱子的移动。
3.4小人移动
该模块用于控制小人的移动,从而推动箱子的移动到达目的地。
3.5功能控制模块
该模块是几个函数功能的集合,包括在屏幕上输出功能信息,关卡数、指定位置状态等。
多关卡代码(思想)示例:
int cur(){
int i,j,k=0;
for(i=0;i<9;i++){
for(j=0;j<11;j++){
if(map[cas][i][j]==4){
}
}
}//遍历整个二
}//计算地图中有多少个终点
判断输赢(思想)示例:
int lose(){
int i,j;
int k=0;
for(i=0;i<14;i++){
for(j=0;j<28;j++){
if(i>0 && j>0 ){
if(map[cas][i][j] == 4||map[cas][i][j] == 7){
if(((map[cas][i-1][j] == 1 || map[cas][i-1][j] == 4 || map[cas][i-1][j] == 7) &&(map[cas][i][j-1] == 1 || map[cas][i][j-1] == 4 || map[cas][i][j-1] == 7))
|| ((map[cas][i][j-1] == 1 || map[cas][i][j-1] == 4 || map[cas][i][j-1] == 7) && (map[cas][i+1][j] == 1 || map[cas][i+1][j] == 4 || map[cas][i+1][j] == 7))
|| ((map[cas][i+1][j] == 1 || map[cas][i+1][j] == 4 || map[cas][i+1][j] == 7) && (map[cas][i][j+1] == 1 || map[cas][i][j+1] == 4 || map[cas][i][j+1] == 7))
|| ((map[cas][i][j+1] == 1 || map[cas][i][j+1] == 4 || map[cas][i][j+1] == 7) && (map[cas][i-1][j] == 1 || map[cas][i-1][j] == 4 || map[cas][i-1][j] == 7))){
k++; }
}
}
}
}
按键退出(思想)示例:
case 'Q':
case 'q':
exit_soko();
按键移动(思想)示例:
void keyDown()
{
//分析按键过程
//定位人在哪里
int i, j;
for (i = 0; i < 填空; i++)
{
for (j = 0; j < 填空; j++)
{
if (map[cas][i][j] == 填空 || map[cas][i][j] == 填空)
break;//beak只能跳出一条语句
}
if (map[cas][i][j] == 填空|| map[cas][i][j] == 填空)
break;//break只能跳出一条语句
}
四、结果验证
游戏退出:
游戏失败:
挑战成功:
所有关卡挑战成功:
总结
基于Linux的推箱子游戏设计在对对程序的编写过程中,设计最大的亮点是模块化设计,将执行功能的各个部分封装成一个个模块,即子函数。
在主函数中编写需要调用功能的子函数名即可执行相应的功能,目的是便于修改,减小对程序大规模的修改,降低了程序编写过程中的出错率。
最大的特点是可以实现对数据的记录修改,在Linux环境下,通过借用标准IO的打开、关闭、读写操作实现对整个游戏是否记忆上次游戏位置,是否退出、开始游戏,进行了操作。
程序在编写的时候考虑到实际的应用需求和视觉审美效果,尽最大程度的在保证程序正常实现各功能的前提下,增加游戏的难度,使游戏的界面呈现出美的效果。
为了提高游戏的难度实现多关卡模式,地图设计的过程中原来的二维数组优化成了三维数组,原来的符号元素,在地图设计中改成了1:2的对称的效果。
设计最大的难点是c语言头文件在不同操作环境下不匹配的问题,要实现相应的功能需要借助已有的头文件,编写函数进行调用。