用C实现贪吃蛇,看看你的C学得怎样?(五)

写在开始

上一篇博客的链接为:用C实现贪吃蛇,看看你的C学得怎样?四

任务清单

本次项目的任务清单暂定如下:
1.创建地图和起始界面(已完成)
2.创建蛇,食物、超级食物投放(已完成)
3.通过键盘控制蛇的移动(已完成)
4.基本功能(蛇吃食物,蛇的增长等)(已完成)
5.判断游戏是否结束(已完成)
6.计算游戏时长
7.加入最高纪录功能
8.游戏等级进度
9.游戏重新开始

代码实现

//1.定义头文件
#include<stdio.h> 
#include<conio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>

//2.宏定义声明
#define X 23       //定义地图大小
#define Y 50

#define UP 0              //0为上,1为下,2为左,3为右                       
#define DOWN 1                           
#define LEFT 2                           
#define RIGHT 3

char map[X][Y];          //地图数组
int s[X*Y][2];          //蛇身坐标数组

int sLength;           //蛇的长度
int direction;         //蛇的方向

bool eated = false;     //标记是否吃到食物
bool GameOver = false;  //判断游戏是否结束

int score = 0;          //得分
int level = 1;
int delay = 250;     //通过延迟控制蛇的行进速度

clock_t start, finish;    //定义计时函数变量
double duration;

//3.调用函数声明
void init()         //程序开始时的初始化操作
{
	srand((unsigned)time(NULL));            //设置随机数种子为现在的时间
	CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);    //隐藏关标
	int i, j;
	for (i = 0; i<X; i++)
	{
		map[i][0] = 1;          //让第一列为1
		map[i][Y - 1] = 1;        //让最后一列为1
	}
	for (j = 0; j<Y; j++)
	{
		map[0][j] = 1;      //让第一行为1
		map[X - 1][j] = 1;    //让最后一行为1
	}
	sLength = 4;          //让蛇的最初长度为4
	s[0][0] = X / 2;
	s[0][1] = Y / 2;        //给蛇头坐标赋值
	for (i = 1; i<4; i++)
	{
		s[i][0] = s[0][0] + i;
		s[i][1] = s[0][1];   //给刚开始的蛇身几个初始坐标
	}
	direction = UP;
}

void gotoxy(int i, int j)        //移动光标
{
	COORD position = { j, i };
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), position);
}

void menu()//初始菜单界面
{
	printf("\n\n\n\n\n\n\t\t\t********************贪吃蛇(实验版)******************\t\t\t\n\n");
	printf("\t\t\t********************欢迎参与游戏测试******************\t\t\t\n\n");
	printf("\t\t\t********************按任意键开始游戏******************\t\t\t\n\n");
	printf("\t\t\t\t\t      职业工具人\n\n");
	printf("\t\t\t\t\t    VERSION:V1.7\n\n");
	printf("\t\t\t\t请先确保处于英语输入法,否则无法进行游戏");
	_getch();
}

void draw_map()   //创建地图   
{
	gotoxy(0, 0);
	int i, j;
	for (i = 0; i<X; i++)
	{
		for (j = 0; j<Y; j++)          //两重for循环遍历数组
		{
			if (map[i][j] == 0)          //为0输出空格
				printf(" ");
			else if(map[i][j]==1)                    //为1输出#
				printf("#");
		}
		printf("\n");               //别忘了换行
	}
}

int check(int ii, int jj)        //判断这个点能不能放食物,可以放返回1,不能放返回0
{
	if (map[ii][jj] == 1)        //如果有障碍物,返回0
		return 0;
	int i;
	for (i = 0; i<sLength; i++)
	{
		if (ii == s[i][0] && jj == s[i][1])      //如果和其中一个蛇身重合,就返回0
			return 0;
	}
	if (ii == 0 || ii == X - 1 || jj == 0 || jj == Y - 1)      //如果在边界上面,返回0
		return 0;
	if (map[ii][jj] == -1)             //若该点有食物,则不生成超级食物                
		return 0;
	return 1;                       //最后筛选 过后的是符合条件的点
}

void food()  //生成食物
{
	int i, j;
	do
	{
		i = rand() % X;                 //生成0~X-1之间的一个数
		j = rand() % Y;                 //生成0~Y-1之间的一个数
	} 
	while (check(i, j) == 0);           //生成点直到满足条件
	map[i][j] = -1;                     //标记为食物
	gotoxy(i, j);
	printf("*");                    //画出食物
}

void s_food()  //生成超级食物
{
	int i, j;
	do
	{
		i = rand() % X;
		j = rand() % Y;
	} 
	while (check(i, j) == 0);
	map[i][j] = 2;
	gotoxy(i, j);
	printf("$");
}

void draw_snake()                //画蛇
{
	int i;
	for (i = 0; i<sLength; i++)
	{
		gotoxy(s[i][0], s[i][1]);        //移动关标到蛇的坐标
		printf("@");                    //在这个位置画蛇
	}
}

void move()                    //控制蛇的移动
{
	int i;
	gotoxy(s[sLength - 1][0], s[sLength - 1][1]);
	printf(" ");                            //在尾巴上面画空格以擦除尾巴
	for (i = sLength - 1; i > 0; i--)  //从尾巴开始,每一个点的位置等于它前面一个点的位置
	{
		s[i][0] = s[i - 1][0];
		s[i][1] = s[i - 1][1];
	}
	switch (direction)
	{
	case UP:
		s[0][0]--;
		break;
	case DOWN:
		s[0][0]++;
		break;
	case LEFT:
		s[0][1]--;
		break;
	case RIGHT:
		s[0][1]++;
		break;
	}
}

bool gameover()             //判断是不是结束游戏
{
	int sX = s[0][0], sY = s[0][1];      //蛇头的x坐标和y坐标
	if (sX == 0 || sX == X - 1 || sY == 0 || sY == Y - 1)
		GameOver = true;
	for (int i = 1; i<sLength; i++)    //判断有没有吃到自己
	{
		if (s[i][0] == sX && s[i][1] == sY)
			GameOver = true;
	}
	return GameOver;
}

int game_level()   //速度等级构建
{
	int Level = score / 100 + 1;
	return Level;
}

void clear_food()   //清除地图数组中由于食物被修改的值
{
	int i, j;
	for (i = 0; i < X;i++)
		for (j = 0; j < Y; j++)
		{
			if (map[i][j] == -1 || map[i][j] == 2)
				map[i][j] = 0;
		}
}

void redraw_snake()   //重置蛇的位置
{
	int i;
	sLength = 4;
	s[0][0] = X / 2;
	s[0][1] = Y / 2;        //给蛇头坐标赋值
	for (i = 1; i<4; i++)
	{
		s[i][0] = s[0][0] + i;
		s[i][1] = s[0][1];   //给刚开始的蛇身几个初始坐标
	}
	direction = UP;
}

void replay()     //一键重玩
{
	system("cls");
	clear_food();
	draw_map();
	redraw_snake();
	food();
	s_food();
	score = 0;           //重置数据
	delay = 250;
	level = 1;
}

void key()       //定义key,用于接受按键键值
{
	if (_kbhit() != 0)          //如果有键盘输入
	{
		char in;
		while (!_kbhit() == 0)  //如果玩家输入了多个按键,以最后一个按键为准
			in = _getch();
		switch (in)
		{
		case 'w':
		case 'W':
			if (direction != DOWN)         //不能后退
				direction = UP;
			break;
		case 's':
		case 'S':
			if (direction != UP)
				direction = DOWN;
			break;
		case 'a':
		case 'A':
			if (direction != RIGHT)
				direction = LEFT;
			break;
		case 'd':
		case 'D':
			if (direction != LEFT)
				direction = RIGHT;
			break;
		case 'p':
		case 'P':
			gotoxy(X, 0);        //将光标移动到下面
			system("pause");
			gotoxy(X, 0);
			printf("                   ");  //消去下面的按任意键继续
			break;
		case 'r':
		case 'R':
			replay();
			break;
		case ' ':
			if (delay == 50)
				return;
			else
				delay -= 10;
			break;
		}
	}
}

void print()       //显示各项操作快捷键和得分
{
	gotoxy(0, Y+ 2);
	printf("贪吃蛇实验版");
	gotoxy(1, Y + 2);
	printf("测试中……");
	gotoxy(4, Y + 2);
	printf("w-->上");
	gotoxy(5, Y + 2);
	printf("s-->下");
	gotoxy(6, Y + 2);
	printf("a-->左");
	gotoxy(7, Y + 2);
	printf("d-->右");
	gotoxy(8, Y + 2);
	printf("p-->暂停");
	gotoxy(9, Y + 2);
	printf("r-->重玩");
	gotoxy(10, Y + 2);
	printf("空格-->加速");
	gotoxy(12, Y + 2);
	printf("得分:%d", score);
	gotoxy(13, Y + 2);
	printf("等级:%d", game_level());
}

void s_time()     //起始时间
{
	start = clock();
}

void e_time()   //结束时间
{   
	finish = clock();
	duration = (double)(finish - start) / CLOCKS_PER_SEC;    //计算持续时间
	printf("游戏时长:%f秒\n ", duration);
}

void getHighScore(int * highscore)
{
	FILE * fp = NULL;   //文件指针,指向成功打开的文件

	//打开文件,以只读模式
	fp = fopen("highscore.txt", "r");

	//如果文件不存在,以写入模式打开文件
	if (fp == NULL)
	{
		fp = fopen("highscore.txt", "w");
		fprintf(fp, "%-10d", *highscore);
	}
	else
		fscanf(fp, "%10d", highscore);
}

void storeHighScore(int highscore)
{
	FILE * fp = NULL;//文件指针,指向成功打开的文件

	fp = fopen("highscore.txt", "w");//文件只读模式
	fprintf(fp, "%-10d", highscore);
	fclose(fp);
}

void high_score()
{
	int highscore = 0;  //存储最高分

	//获取历史最高分
	getHighScore(&highscore);
	printf("Original high score: %d\n\n", highscore);
	if (score > highscore)
		highscore = score;
	storeHighScore(highscore);
}

void restart()   //结束后重新开始
{
		GameOver = false;
		replay();
}

//4.主函数体
int main()
{
	init();                     //程序开始时的初始化操作
	menu();
	s_time();
	system("cls");          //清除屏幕内容
	draw_map();                  //画地图
	food();                     //画食物
	s_food();
loop:
	{
		while (1)
		{
			draw_snake();                //画蛇
			print();
			Sleep(delay);           //等待一段时间
			key();
			move();                     //移动蛇(主要是修改蛇身数组的数据)
			if (map[s[0][0]][s[0][1]] == -1) //如果蛇头碰到食物,就重新投放食物,并且把食物点重置为0
			{
				eated = true;             //标记已经吃到食物
				score += 20;
				sLength++;
				if (game_level() != level)
				{
					delay -= 10;
					level++;
				}
				eated = false;        //设置为false,不然无限变长
				food();
				map[s[0][0]][s[0][1]] = 0;  //去掉食物
			}
			if (map[s[0][0]][s[0][1]] == 2)
			{
				eated = true;
				score += 50;
				sLength += 2;
				if (game_level() != level)
				{
					delay -= 10;
					level++;
				}
				eated = false;        //设置为false,不然无限变长
				s_food();
				map[s[0][0]][s[0][1]] = 0;
			}
			if (gameover())
			{
				system("cls");          //清除屏幕内容
				printf("Game Over\n");
				printf("最终得分:%d\n\n", score);
				e_time();
				high_score();
				break;
			}
		}
	}
	printf("双按空格退出,双按r键重玩\n");
	if (_getch() == ' ')
		return 0;
	else if (_getch()=='r'||_getch()=='R')
	{
		restart();
		goto loop;
	}
}

总结

本次的博客将最后的四个功能点都实现了,对于刚学完C的小伙伴来说,比较复杂的功能点是获取最高分写入文件,运用了指针以及文件操作。指针是C的灵魂,也是最难懂得部分,博主目前也没能够完全地悟透,在这里就借助了一部分百度的力量实现了这个功能。

此外,在重玩的部分也有点不太优雅,使用了goto关键词,其实这个关键字是在开发中被禁止使用的,但是博主能力有限,没能够避开goto来实现的方法,如果有想法的小伙伴,可以在留言区里提供下思路,帮助博主重构gameover后重玩游戏的算法!

结语

如果说这篇文章有让你学到一定的知识的话,不妨点个赞和关注,让博主能够看到,如果讲解中有什么错误和疏忽,也劳烦在评论中指出或提问,博主会第一时间进行更新和答复,谢谢!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值