C语言实现贪吃蛇(四)----游戏存档读档(文件操作)
2016-11-18 19:50
846 查看
前言:
对于C语言的初学者来说,可能会觉得文件操作是一个比较陌生的领域,但实际上无论编写怎样的程序文件操作都是必需的。在操作系统中,所有的外围设备(包括键盘和显示器)都被看作是文件系统中的文件,因此所有的输入/输出都要通过读文件/写文件完成。启动一个C语言程序时,操作环境负责打开标准输入,标准输出,标准错误3个文件,并将这3个文件的指针提供给该程序。而我们熟知的 getchar 函数 putchar 函数其实就是通过这些文件指针实现的。这样说大家应该对文件操作的重要性有了一个初步的了解,接下来我们就运用文件操作的相关知识,为贪吃蛇游戏加上存档读档功能。如何存储画面的信息呢,在贪吃蛇游戏中,我们只需要存储蛇的坐标、食物的坐标以及蛇的前进方向。我们可以利用标准库里的fopen,fwrite,fread三个函数完成这一要求。如果大家记不住这几个函数,请自行百度。
PS:本篇博客中将要实现的游戏存档读档功能是基于 《C语言实现贪吃蛇(三)—-结构+链表实现》 这个贪吃蛇版本的。如果大家不是从那篇博客过来的话,务必回去看看。
一、游戏存档:
在编写存档函数 save() 前,我们先来看看新增的一些需要用到的变量定义:FILE * mem; //游戏存档文件句柄 char dir = UP; //因为在save函数里要用到,把原来定义在主函数里的dir移出来,并初始化为向上 void save(void); //游戏存档函数
save() 游戏存档函数:
void save(void){ snake_node * p = NULL; //打开存档文件 if((mem = fopen("memory.txt","w+")) == NULL){ fprintf(stderr,"Can't open memory.txt!\n"); exit(1); } //保存蛇长度 fwrite(&len,sizeof(len),1,mem); //保存方向 fwrite(&dir,sizeof(dir),1,mem); //保存食物坐标(结构体是值类型) fwrite(&food,sizeof(food),1,mem); //保存蛇坐标 for(p = snake_head;p != NULL;p = p->next){ fwrite(&(p->cor),sizeof(p->cor),1,mem); } fclose(mem); }
二、游戏读档:
编写 load() 读档函数前只需声明一下自己即可://读档 void load(void);
load() 游戏读档函数:
void load(void){ snake_node * p,* q; //这里的 p、q 无非跟之前的 last、current 一样 int m = 1,i,j; //读取游戏存档 if((mem = fopen("memory.txt","r")) == NULL){ fprintf(stderr,"No Game Cache Here!\n"); exit(2); } //读取蛇长度 fread(&len,sizeof(len),1,mem); //读取方向 fread(&dir,sizeof(dir),1,mem); //读取食物坐标(结构体是值类型) fread(&food,sizeof(food),1,mem); //读取蛇头坐标 snake_head = (snake_node *)malloc(sizeof(snake_node)); fread(&(snake_head->cor),sizeof(COORD),1,mem); snake_head->next = NULL; m ++; //准备读取第二节 //获取蛇身体坐标 p = snake_head; while(m <= len){ q = (snake_node *)malloc(sizeof(snake_node)); fread(&(q->cor),sizeof(COORD),1,mem); q->next = NULL; p->next = q; p = q; m ++; } //打印围墙 for (j = 0; j < 17; j ++) { for (i = 0; i < 17; i ++) if (i == 0 || i == 16 || j == 0 || j == 16) { putchar('#'); } else { putchar(' '); } putchar('\n'); } //打印蛇身 for (p = snake_head; p != NULL; p = p->next) { gotoxy(p->cor); putchar('*'); } //打印食物 gotoxy(food); putchar('$'); fclose(mem); }
三、存档与读档:
万事俱备、只欠东风。现在我们已经有了存档读档函数了,接下来就是考虑什么时候需要存储游戏记录了。显然,只有在游戏中途退出的时候需要存储。我们可以在游戏进行的时候通过按 ESC 键中途退出游戏,并保存游戏进度。
在此之前,我们需要对 ESC 键定义:
#define ESC 27
对 get_dir() 函数进行稍微的改进以达到该目的:
int get_dir(int old_dir){ int new_dir = old_dir; //用kbhit()与getch()组合实现键盘响应 //kbhit() 检查当前是否有键盘输入,若有则返回一个非0值,否则返回0 //getch() 用ch=_getch();会等待你按下任意键之后,把该键字符所对应的ASCII码赋给ch,再执行下面的语句。 if(_kbhit()){ //按ESC存档并退出 if (_getch() == ESC){ save(); //存档 free_node(snake_head); system("cls"); printf("Caching...\n"); exit(0); }; new_dir = _getch(); //getch()函数要使用两次,原因是因为第一次返回的值指示该键扩展的字符,第二次调用才返回实际的键代码 //如果蛇身长度大于1,则不能回头,如果摁回头方向,则按原来方向走 if(len > 1 && (abs(new_dir - old_dir) == 2 || abs(new_dir - old_dir) == 8)){ new_dir = old_dir; } } return new_dir; }
注意这里ESC应该放在第一个getch()处检测。ESC不同于方向键,第一次便返回键值,方向键需要两次。
既然加入了存档与读档功能,那么就必须得有个 新建游戏 与 继续游戏 的选择界面。我们在主函数的循环开始前加入choose函数实现这个界面。
在实现 choose() 函数前,先来看看需要的变量等的声明
//新建游戏与继续游戏选项的显示位置 COORD s_option = { 5,5 },c_option = { 5,7 }; //按回车键选择 #define ENTER 13 //选择是新建游戏还是继续游戏 void choose(void);
choose() 函数代码:
void choose(void) { int key, option = 1; //默认是新建游戏 gotoxy(s_option); printf("->NEW GAME"); gotoxy(c_option); printf(" CONTINUE"); while ((key = _getch()) != ENTER) { if (key == UP && option > 1) { option --; } if (key == DOWN && option < 2) { option ++; } if (option == 1) { gotoxy(s_option); printf("->"); gotoxy(c_option); printf(" "); } else { gotoxy(c_option); printf("->"); gotoxy(s_option); printf(" "); } } system("cls"); if (option == 1) { init_game(); //初始化新游戏 } else { load(); //加载存档 } }
option的值n代表第n个选项。
由于添加了存档读档功能,因此主函数也得有相应的改变,改变如下:
int main(void) { choose(); //决定是新建游戏还是继续游戏 while(1){ dir = get_dir(dir); //获取方向(我们摁下的方向) move_snake(dir); //移动蛇身 if(!isalive()){ //判断蛇的生命状态 break; } } //清除屏幕 system("cls"); printf("Game Over!\n"); //释放申请的内存空间 free_node(snake_head); return 0; }
到了这一步我们的程序已经算是完成了。
但是在选择是新建游戏还是继续游戏那里,你会发现光标会一直在闪烁,其实这个情况跟前面我们遇到的贯标一直跟着蛇尾是一样的,前面我们的解决方法是将光标定位到左下角:
//避免光标一直跟着蛇尾(或食物 ) COORD foot = {0,17}; //将光标置于左下角 gotoxy(foot);
在这里原作者给出了一个更好的解决方法:直接将光标隐藏掉!
为了实现隐藏光标,我们需要修改 gotoxy() 函数:
void gotoxy(COORD pt) { //句柄 HANDLE hout; //GetStdHandle函数获取一个指向特定标准设备的句柄,包括标准输入,标准输出和标准错误。 //STD_OUTPUT_HANDLE正是代表标准输出(也就是显示屏)的宏 hout = GetStdHandle(STD_OUTPUT_HANDLE); //SetConsoleCursorPosition函数用于设置控制台光标的位置 SetConsoleCursorPosition(hout, pt); //隐藏光标 CONSOLE_CURSOR_INFO cursor_info = { 1,0 }; SetConsoleCursorInfo(hout, &cursor_info); }
本博客参考自C语言实现贪吃蛇之文件操作篇 。
好了,到了这里这个程序已经很不错了。但是原作者似乎还不够过瘾,毕竟命令行黑乎乎的。于是乎原作者就搞了个贪吃蛇图形界面版。下一篇博客中我将带着大家来看看图形界面版怎么实现。
相关文章推荐
- java速度入四.文件的基本操作及猜数字游戏的实现
- Linux环境下C语言实现贪吃蛇游戏
- Ubuntu下C语言实现贪吃蛇游戏
- C语言实现文件、文件夹的操作
- 纯C语言实现贪吃蛇游戏(VC6.0)
- C语言实现贪吃蛇游戏
- linux文件操作指令的c语言代码实现
- c语言实现最简单的写文件操作
- 贪吃蛇游戏的C语言实现
- Ubuntu下C语言实现以子进程号命名的文件及文件夹的新建、删除等操作。
- 【C语言】没事可以试试这个小程序,使用文件操作,模拟实现一个简单的文件拷贝工具!
- c语言以字符形式实现文件的读写操作
- Linux系统遍历文件夹 获取文件大小的操作(C语言实现)
- [黑马IOS自学第八篇]C语言文件操作,通讯录实现,代码
- 文件操作,及文件操作时的权限设置,快速实现文件拷贝,C语言常用IO函数
- 自己实现的C语言string.h 头文件的字符串函数与几个内存操作函数
- c语言实现读取ini文件操作
- C语言游戏之贪吃蛇--链表实现
- 基于C语言实现的贪吃蛇游戏完整实例代码