前言:

对于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语言实现贪吃蛇之文件操作篇 。

好了,到了这里这个程序已经很不错了。但是原作者似乎还不够过瘾,毕竟命令行黑乎乎的。于是乎原作者就搞了个贪吃蛇图形界面版。下一篇博客中我将带着大家来看看图形界面版怎么实现。

C语言实现贪吃蛇(四)----游戏存档读档(文件操作)相关推荐

  1. C语言实现贪吃蛇小游戏1.0

    C语言实现贪吃蛇小游戏1.0 贪吃蛇游戏要有三个东西:边框.蛇.食物 还有两个灵魂的东西:光标的移动与按键监控 一.光标的移动 在我看来在控制台上移动光标画图是实现这个小游戏的灵魂了,在这之前我一直以 ...

  2. 安卓c语言自动补全软件吾爱,C语言实现贪吃蛇小游戏

    本文实例为大家分享了C语言实现贪吃蛇小游戏的具体代码,供大家参考,具体内容如下 一.程序实现的原理: 1.构造蛇身:定义一个坐标数组,存放的是蛇的每一节蛇身所在的坐标位置.这样就将移动蛇身的操作转换为 ...

  3. 【笔记】用vs2017 c语言写 贪吃蛇 小游戏

    目录 文章目录 前言 一.贪吃蛇游戏 设计逻辑 二.详细介绍 1.设置界面(界面大小,背景颜色) 2.设置贪吃蛇的身体并显示 3.使之能够移动并能够穿墙 4.添加食物并让蛇吃掉 5.设置背景音乐 完整 ...

  4. C语言实现贪吃蛇小游戏!(超简单详细)详细思路+源码分享

    贪吃蛇(也叫做贪食蛇)游戏是一款休闲益智类游戏,有PC和手机等多平台版本.既简单又耐玩.该游戏通过控制蛇头方向吃蛋,从而使得蛇变得越来越长. 我们的今天的目标就是:用C语言来实现一个贪吃蛇项目,也不用 ...

  5. C语言实现贪吃蛇小游戏(详解)

    目录 一.程序实现的原理: 二.预备知识 1.控制台窗口 2.通过代码将光标移动到控制台指定位置 三. 程序框架 四.具体操作 1.定义相关头文件 2.定义函数结构以及相关属性 五.完成各代码块 1. ...

  6. 控制台版贪吃蛇小游戏 C语言

    写在前面 最近我们C语言的课设快开始了,开始前刚好有时间就写了一下C语言的贪吃蛇小游戏(单链表实现),包含了经典模式和无边界模式 ,网上查了设置颜色 和 改变光标位置 还有 用方向键控制 的函数,第一 ...

  7. java 实现贪吃蛇小游戏

    在贪吃蛇游戏中,贪吃蛇的移动可以看作是一个先进先出的队列(从蛇尾的角度看),因此我们在C语言实现贪吃蛇小游戏一文中使用了循环链接表作为队来存储蛇身节点,在 python实现贪吃蛇小游戏一文中,使用列表 ...

  8. matlab 实现贪吃蛇小游戏

    在贪吃蛇游戏中,贪吃蛇的移动可以看作是一个先进先出的队列(从蛇尾的角度看),因此我们在C语言实现贪吃蛇小游戏一文中使用了循环链接表作为队来存储蛇身节点,在 python实现贪吃蛇小游戏一文中,使用列表 ...

  9. c语言安卓贪吃蛇代码下载,C语言贪吃蛇代码

    c语言编写贪吃蛇源代码,简单易懂,文件为VC源代码.如果你正在学习c语言,就来下载吧.很经典的 C语言贪吃蛇代码部分 #include #include #include#include #defin ...

  10. 手把手教你做一个Java贪吃蛇小游戏

    大家好,我是孙不坚1208,这篇博客给大家分享一下:如何做一个贪吃蛇小游戏(Java版)的exe应用程序,希望能给需要帮助的朋友带来方便. 手把手教你做一个Java贪吃蛇小游戏的exe应用程序 一.J ...

最新文章

  1. Linux 操作系统原理 — 内核态与用户态
  2. 统计特性和概率估计-1 (数学推导与证明)
  3. python getopts_getopts用法
  4. linux重要的目录之etc
  5. 大数据分析-第八章 推荐系统
  6. codeBook背景建模
  7. Arcgis操作系列一:shp矢量数据的面积计算
  8. 手写操作系统2——编写MBR主引导程序
  9. Ubuntu 16.04 单显卡安装Nvidia驱动+GTX750显卡安装CUDA 9.1+cuDNN 7.1.3
  10. Lesson 17.11 案例一:SVHN街道实景门牌识别
  11. 魂斗罗进化革命 塞班JAVA版_魂斗罗之进化革命_JAVA游戏免费版下载_7723手机游戏[www.7723.cn]...
  12. mac改变ipv4地址无法上网
  13. Leetcode 算法面试冲刺 热题 HOT 100 刷题(406 416 437 438 448)(六十九)
  14. ubuntu20.04安装到u盘+配置美化
  15. 六:分布式架构存储设计
  16. Python 线程启动和关闭
  17. Nodejs之解决接口跨域问题
  18. arcgis desktop和arcgis engin连接postgresql数据库连接需要的类库
  19. 从qq空间看Tencent
  20. java节假日算法_基于Java代码实现判断春节、端午节、中秋节等法定节假日的方法...

热门文章

  1. 浅析经典JVM垃圾收集器-Serial/ParNew/Parallel Scavenge/Serial Old/Parallel Old/CMS/G1
  2. 2018年Android面试题含答案--适合中高级(上)
  3. 听课笔记-名校启程大学规划课
  4. Oracle和MySQL新增只有查询权限用户
  5. java判断101-200之间有多少个素数_并输出所有素数_编程基础练习:题目:判断101-200之间有多少个素数,并输出所有素数。 - 菜鸟头头...
  6. android 5.1一键root工具箱,最新的安卓5.1.1 ROOT教程(不需要刷第三方内核)
  7. jenkins编译java项目时无法读取pom.xml文件
  8. 【问题解决方法】每次关闭XAMPP都会出现报错:Error:Cannot create file ‘E:\XAMPP\xampp-control.ini’
  9. 在线简洁Logo制作
  10. 我的并行计算之路(二)MPI点对点通信MPI_Send和MPI_Recv