贪吃蛇小游戏(C语言实现简易版)

大一作品,贪吃蛇小游戏具有菜单功能,难度选择功能,记录功能,暂停功能,缺陷在于不能将游戏分数存入文件中,游戏记录只在当次游戏时有效。

注意事项:在设置地图和距离的时候注意,要使蛇头的坐标和果子的坐标可以重合,在本文中代码,蛇头以及果子的横坐标均为偶数。在运行中,如果出现蛇吃了果子以后就不在出现新果子的情况可以考虑是否使果子与蛇的坐标错开导致两者目测坐标重合,而不是真正重合。

目录

完整代码

头文件

定义数据 

构建所需数据存储单位

自定义函数总览

全局变量设置

游戏介绍模块

菜单以及中转模块

启动游戏模块

设置游戏难度模块

游戏记录模块

光标模块

地图模块

食物模块

蛇模块

移动模块(包含暂停功能)

结束模块

主程序


完整代码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<time.h>//随机种子来出现食物
#include<conio.h>//监听键盘输入
#include<windows.h>//为了使用gotoxy(光标移动函数)

#define guozi "■"   //果子
#define qiangbi "□"//墙体
#define head  "⊙"//蛇头
#define body "●"//蛇身
#define height 3//初始长度
#define chang 70//地图大小
#define kuan 30//地图大小
#define weiyi 21//初始化时地图位移距离

struct snake//蛇的数据结构
{
	int x[7920];//蛇坐标
	int y[7920];//蛇坐标
	int changdu;//蛇长
	int now_record = 0;
}snake;


struct food//食物的坐标
{
	int x;
	int y;
}food;


  struct record
{
	int first_record=0;
	int second_record=0;
	int third_record=0;
}record;

void jieshao();//游戏介绍
void menu();//菜单
void select(int choice);//中转
void start(int chanllage);//启动游戏
int change(int chanllage);//选择游戏难度
void remember();//游戏记录
void recordnow();//记录结果
void gotoxy(int x, int y);//光标移动
void HideCursor();//隐藏光标
void mapmode();//地图模块
void foodmode();//果子模块
void snakemode();//蛇模块
void movemode();//移动模块
int endmode();//结束模块


int i = 0;
int need = 1;
int number = 1;
int choice;
int chanllage;
int differ=300;
int hengzuobiao [33] = {22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,68,70,72,74,76,78,80,82,84,86,88};
char direct = 'a';


void jieshao()//游戏介绍
{
	printf("\n		******************菜单栏*****************\n");
	printf("\t\t*\t      ①上 W/w                    *\n");
	printf("\t\t*\t      ②下 S/s                    *\n");
	printf("\t\t*\t      ③左 A/a                    *\n");
	printf("\t\t*\t      ④右 D/d                    *\n");
	printf("\t\t*\t      ⑤按任意键返回              *\n");
	printf("		*****************************************\n");

	char anjian;
	anjian = _getch();//等待返回

}


void menu()//菜单
{
	while(number) //循环菜单,直到退出
	{
		int ture=1;
		if (_kbhit())//防止上一次游戏中的操作带到下一次游戏中
		{
			fflush(stdin);
		}
		system("cls");
		do {
			printf("\n		******************菜单栏*****************\n");
			printf("\t\t*\t      ①开始游戏                    *\n");
			printf("\t\t*\t      ②难度选择                    *\n");
			printf("\t\t*\t      ③记录                        *\n");
			printf("\t\t*\t      ④游戏介绍                    *\n");
			printf("\t\t*\t      ⑤退出游戏                    *\n");
			printf("		*****************************************\n");
			printf("\n");
			printf("\t\t请输入选项:[\t]\b\b\b");
			ture=scanf("%d", &choice);
			if (ture != 1)//
			{
				while (getchar() != '\n')
				{
					gotoxy(30,8);
					printf("  ]");
					gotoxy(30, 8);
					ture=scanf("%d", &choice);

				}
			}
		} while (choice > 5 || choice < 1);//只在选择1到4时执行下一步
		select(choice);
	}
}


void select(int choice)//中转模块
{
	 snake.now_record = 0;
	switch (choice)
	{
	case 1:
		system("cls");//清除菜单
		need = 1;//确保果子生成
		direct = 'a';
		start(differ);
		recordnow();
		return;
	case 2:
		system("cls");//清除菜单
		differ=change(chanllage);//得到Sleep()的参数
		return;
	case 3:
		system("cls");//清除菜单
		remember();
		return;
	case 4:
		system("cls");//清除菜单
		jieshao();
		return;
	case 5:
		system("cls");//清除菜单
		printf("\n\t\t谢谢使用(^V^)!\n");
		number = 0;		
		Sleep(500);
		exit(0);
	}
}


void start(int chanllage)//启动游戏模块
{
	mapmode();//生成地图
	while (1)//开始游戏
	{
		Sleep(chanllage);
		if (need)//判断是否需要生成果子
		{
			foodmode();
		}

		movemode();//移动

		if (endmode())//判断游戏是否结束
		{
			system("cls");
		    gotoxy(chang / 2 + weiyi, kuan / 2);
	        printf("\n\n\t\t\tgame over!");
			printf("你的分数:%d", snake.now_record);
		    Sleep(500);
		 	return;
		}
	}
}


int change(int chanllage)//修改难度。即修改Sleep()的参数
{
	int which;
	printf("\n		******************菜单栏*****************\n");
	printf("\t\t*\t      ①有脚就行                   *\n");
	printf("\t\t*\t      ②就这                       *\n");
	printf("\t\t*\t      ③有手就行                   *\n");
	printf("\t\t*\t      ④痛苦面具                   *\n");
	printf("		*****************************************\n");
	printf("\n");
	printf("\t\t请输入选项:[\t]\b\b\b");
	scanf("%d", &which);
	switch (which)
	{
	case 1:
		chanllage = 300;
		break;
	case 2:
		chanllage = 150;
		break;
	case 3:
		chanllage = 50;
		break;
	case 4:
		chanllage = 20;
		break;
	}
	return chanllage;
}


void remember()//记录模块
{
	char next;
	printf("\t!\t\t\t\t第一名:%d\n",record.first_record);
	printf("\t\t\t\t\t第二名:%d\n", record.second_record);
	printf("\t\t\t\t\t第三名:%d\n", record.third_record);
	gotoxy(0, 0);
	
	next = _getch();
	Sleep(500);
}


void recordnow()//比较分数
{
	int one=0, two;
	if ( snake.now_record >record.first_record)//判断第一名,修改排位
	{
		one=record.first_record ;
		two=record.second_record ;
		record.first_record = snake.now_record;
		record.second_record = one;
		record.third_record = two;
		return;
	}

	if (record.second_record < snake.now_record && snake.now_record < record.first_record);//判断第二名,修改排位
	{
		two=record.second_record ;
		record.second_record = snake.now_record;
		record.third_record = two;
		return;
	}
	if (record.second_record > snake.now_record && snake.now_record > record.third_record)//判断第三名,修改排位
	{
		record.third_record =snake.now_record;
		return;
	}
}



void gotoxy(int x, int y)//移动光标
{
	COORD pos = { x, y };
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorPosition(hOut, pos);
}



void HideCursor()//隐藏光标
{
	CONSOLE_CURSOR_INFO curInfo; 
	curInfo.dwSize = 1; 
	curInfo.bVisible = FALSE; 
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); 
	SetConsoleCursorInfo(handle, &curInfo); 
}


void mapmode()//加载地图
{
	for (i = 0 + weiyi; i < weiyi + chang; i += 2)//循环打印上下墙体
	{
		gotoxy(i, 0);
		printf(qiangbi);

		gotoxy(i, kuan-1);
		printf(qiangbi);
	}

	for (i = 0; i < kuan; i++)//循环打印左右墙体
	{
		gotoxy(weiyi, i);
		printf(qiangbi);

		gotoxy(weiyi + chang, i);
		printf(qiangbi);
	}

	gotoxy(1, 0);
	printf("分数:%d",snake.now_record);

	gotoxy(1, 0);

	snakemode();
}


void foodmode()//生成果子
{
	srand(time(NULL));
	int creat_one = 1;//确认产生果子的需要
	food.x =hengzuobiao[ rand() % 33+0];
	food.y = rand() % 26+2;

	
		if (food.x % 2 == 0)
		{
			for (i = 0; i < snake.changdu; i++)
			{
				if (snake.x[i] == food.x && snake.y[i] == food.y)//确认坐标不在蛇体内
				{
					creat_one = 0;
				}
				if (creat_one)//生成果子
				{
					gotoxy(food.x, food.y);
					printf(guozi);

					need = 0;//不需要在下一循环生成果子

					gotoxy(0, 0);
					
				}
			}
		}
	
}


void snakemode()//产生初始蛇
{
	snake.changdu = height;
	snake.x[0] = chang / 2 + weiyi;
	snake.y[0] = kuan / 2;

	gotoxy(snake.x[0], snake.y[0]);//生成蛇头
	printf(head);

	for (i = 1; i < snake.changdu; i++)//循环生成蛇身
	{
		snake.x[i] = snake.x[i - 1] + 2;
		snake.y[i] = snake.y[i - 1];

		gotoxy(snake.x[i], snake.y[i]);
		printf(body);
	}

	gotoxy(0, 0);
}


void movemode()//移动模块
{
     char shangci;
	 shangci = direct;
	if (_kbhit())
	{
		fflush(stdin);
		direct = _getch();
		if (direct != 'w' && direct != 's' && direct != 'd' && direct != 'a'&&direct != 'W' && direct != 'S' && direct != 'D' && direct != 'A' && direct!=32)
		{
			direct = shangci;
		}
	}

	gotoxy(snake.x[snake.changdu - 1], snake.y[snake.changdu - 1]);//覆盖蛇尾
	printf("  ");

	for (i = snake.changdu - 1; i > 0; i--)//循环生成蛇身
	{
		snake.x[i] = snake.x[i - 1];
		snake.y[i] = snake.y[i - 1];

		gotoxy(snake.x[i], snake.y[i]);
		printf(body);
	}
	
	first:
	switch (direct)//确认蛇的移动方向
	{
	case 'w':
	case 'W':
		snake.y[0]--;
		break;
	case 's':
	case 'S':
		snake.y[0]++;
		break;
	case 'd':
	case 'D':
		snake.x[0] += 2;
		break;
	case 'a':
	case 'A':
		snake.x[0] -= 2;
		break;
	case 32:
		
		char xuanze;
		gotoxy(95, 10);
		printf("①继续游戏");
		gotoxy(95, 11);
		printf("②退出游戏");
		xuanze = _getch();
		system("pause>nul");
		if (xuanze =='1' )
		{
			gotoxy(95, 10);
			printf("            ");
			gotoxy(95, 11);
			printf("             ");
			direct = shangci;
			goto first;
		}
		else if(xuanze=='2')
		{
			system("cls");
			gotoxy(chang / 2 + weiyi, kuan / 2);
			Sleep(500);
			return;
		}
	}

	gotoxy(snake.x[0], snake.y[0]);//生成蛇头
	printf(head);

	gotoxy(0, 0);

	if (snake.x[0] == food.x && snake.y[0] == food.y)//判断是否吃到果子
	{
		snake.changdu++;
		snake.now_record++;
		gotoxy(1, 0);
		printf("        ");
		gotoxy(4, 0);
		printf("分数:%d",snake.now_record);
		need = 1;
	}
}


int endmode()//判断是否结束
{
	//撞墙
	if (snake.x[0] == weiyi + chang-1|| snake.x[0] == weiyi-1 || snake.y[0] == 0 || snake.y[0] == kuan - 1)
	{
		return 1;
	}
	//自杀
	for (i = 1; i <= snake.changdu; i++)
	{
		if (snake.x[i] == snake.x[0] && snake.y[i] == snake.y[0])
		{
			return 1;
		}
	}

	return 0;
}


int main()//主程序
{
    HideCursor();
	menu();

	return 0;
}

头文件

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<time.h>//随机种子来出现食物
#include<conio.h>//监听键盘输入
#include<windows.h>//为了使用gotoxy(光标移动函数)

定义数据 

方便改变各种数据

#define guozi "■"   //果子
#define qiangbi "□"//墙体
#define head  "⊙"//蛇头
#define body "●"//蛇身
#define height 3//初始长度
#define chang 70//地图大小
#define kuan 30//地图大小
#define weiyi 21//初始化时地图位移距离

构建所需数据存储单位

需要构建三个结构体,一个用于储存蛇的数据,一个用于储存果子的坐标,一个用于记录游戏分数

struct snake//蛇的数据结构
{
	int x[7920];//蛇坐标
	int y[7920];//蛇坐标
	int changdu;//蛇长
	int now_record = 0;
}snake;


struct food//食物的坐标
{
	int x;
	int y;
}food;


  struct record
{
	int first_record=0;
	int second_record=0;
	int third_record=0;
}record;

自定义函数总览

将贪吃蛇分为不同模块进行编译

void jieshao();//游戏介绍
void menu();//菜单
void select(int choice);//中转
void start(int chanllage);//启动游戏
int change(int chanllage);//选择游戏难度
void remember();//游戏记录
void recordnow();//记录结果
void gotoxy(int x, int y);//光标移动
void HideCursor();//隐藏光标
void mapmode();//地图模块
void foodmode();//果子模块
void snakemode();//蛇模块
void movemode();//移动模块
int endmode();//结束模块

全局变量设置

有一些变量可以一起设置,然后再在不同的函数进程中重新赋值,会方便不少

int i = 0;
int need = 1;
int number = 1;
int choice;
int chanllage;
int differ=300;
int hengzuobiao [33] = {22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,68,70,72,74,76,78,80,82,84,86,88};
char direct = 'a';

游戏介绍模块

一款游戏自然少不了介绍部分

void jieshao()//游戏介绍
{
	printf("\n		******************菜单栏*****************\n");
	printf("\t\t*\t      ①上 W/w                    *\n");
	printf("\t\t*\t      ②下 S/s                    *\n");
	printf("\t\t*\t      ③左 A/a                    *\n");
	printf("\t\t*\t      ④右 D/d                    *\n");
	printf("\t\t*\t      ⑤按任意键返回              *\n");
	printf("		*****************************************\n");

	char anjian;
	anjian = _getch();//等待返回

}

菜单以及中转模块

作为一个游戏,菜单使必不可少的,注意如果不适用getchar(),在接收到非目标按键值时菜单会不断刷新,无限循环

vvoid menu()//菜单
{
	while(number) //循环菜单,直到退出
	{
		int ture=1;
		if (_kbhit())//防止上一次游戏中的操作带到下一次游戏中
		{
			fflush(stdin);
		}
		system("cls");
		do {
			printf("\n		******************菜单栏*****************\n");
			printf("\t\t*\t      ①开始游戏                    *\n");
			printf("\t\t*\t      ②难度选择                    *\n");
			printf("\t\t*\t      ③记录                        *\n");
			printf("\t\t*\t      ④游戏介绍                    *\n");
			printf("\t\t*\t      ⑤退出游戏                    *\n");
			printf("		*****************************************\n");
			printf("\n");
			printf("\t\t请输入选项:[\t]\b\b\b");
			ture=scanf("%d", &choice);
			if (ture != 1)//
			{
				while (getchar() != '\n')
				{
					gotoxy(30,8);
					printf("  ]");
					gotoxy(30, 8);
					ture=scanf("%d", &choice);

				}
			}
		} while (choice > 5 || choice < 1);//只在选择1到4时执行下一步
		select(choice);
	}
}


void select(int choice)//中转模块
{
	 snake.now_record = 0;
	switch (choice)
	{
	case 1:
		system("cls");//清除菜单
		need = 1;//确保果子生成
		direct = 'a';
		start(differ);
		recordnow();
		return;
	case 2:
		system("cls");//清除菜单
		differ=change(chanllage);//得到Sleep()的参数
		return;
	case 3:
		system("cls");//清除菜单
		remember();
		return;
	case 4:
		system("cls");//清除菜单
		jieshao();
		return;
	case 5:
		system("cls");//清除菜单
		printf("\n\t\t谢谢使用(^V^)!\n");
		number = 0;		
		Sleep(500);
		exit(0);
	}
}

启动游戏模块

将启动游戏所需额的各种模块按顺序使用

void start(int chanllage)//启动游戏模块
{
	mapmode();//生成地图
	while (1)//开始游戏
	{
		Sleep(chanllage);
		if (need)//判断是否需要生成果子
		{
			foodmode();
		}

		movemode();//移动

		if (endmode())//判断游戏是否结束
		{
			system("cls");
		    gotoxy(chang / 2 + weiyi, kuan / 2);
	        printf("\n\n\t\t\tgame over!");
			printf("你的分数:%d", snake.now_record);
		    Sleep(500);
		 	return;
		}
	}
}

设置游戏难度模块

游戏的难度因人而异,因此,多设置几个难度会更加人性化,也更有趣

int change(int chanllage)//修改难度。即修改Sleep()的参数
{
	int which;
	printf("\n		******************菜单栏*****************\n");
	printf("\t\t*\t      ①有脚就行                   *\n");
	printf("\t\t*\t      ②就这                       *\n");
	printf("\t\t*\t      ③有手就行                   *\n");
	printf("\t\t*\t      ④痛苦面具                   *\n");
	printf("		*****************************************\n");
	printf("\n");
	printf("\t\t请输入选项:[\t]\b\b\b");
	scanf("%d", &which);
	switch (which)
	{
	case 1:
		chanllage = 300;
		break;
	case 2:
		chanllage = 150;
		break;
	case 3:
		chanllage = 50;
		break;
	case 4:
		chanllage = 20;
		break;
	}
	return chanllage;
}

游戏记录模块

玩游戏的时候比一比分数可以看到自己和别人的差距

void remember()//记录模块
{
	char next;
	printf("\t!\t\t\t\t第一名:%d\n",record.first_record);
	printf("\t\t\t\t\t第二名:%d\n", record.second_record);
	printf("\t\t\t\t\t第三名:%d\n", record.third_record);
	gotoxy(0, 0);
	
	next = _getch();
	Sleep(500);
}


void recordnow()//比较分数
{
	int one=0, two;
	if ( snake.now_record >record.first_record)//判断第一名,修改排位
	{
		one=record.first_record ;
		two=record.second_record ;
		record.first_record = snake.now_record;
		record.second_record = one;
		record.third_record = two;
		return;
	}

	if (record.second_record < snake.now_record && snake.now_record < record.first_record);//判断第二名,修改排位
	{
		two=record.second_record ;
		record.second_record = snake.now_record;
		record.third_record = two;
		return;
	}
	if (record.second_record > snake.now_record && snake.now_record > record.third_record)//判断第三名,修改排位
	{
		record.third_record =snake.now_record;
		return;
	}
}

光标模块

为了使游戏更加美观,流畅,需要对光标进行位移和隐藏

void gotoxy(int x, int y)//移动光标
{
	COORD pos = { x, y };
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleCursorPosition(hOut, pos);
}



void HideCursor()//隐藏光标
{
	CONSOLE_CURSOR_INFO curInfo; 
	curInfo.dwSize = 1; 
	curInfo.bVisible = FALSE; 
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); 
	SetConsoleCursorInfo(handle, &curInfo); 
}

地图模块

地图使最基本的要素之一

void mapmode()//加载地图
{
	for (i = 0 + weiyi; i < weiyi + chang; i += 2)//循环打印上下墙体
	{
		gotoxy(i, 0);
		printf(qiangbi);

		gotoxy(i, kuan-1);
		printf(qiangbi);
	}

	for (i = 0; i < kuan; i++)//循环打印左右墙体
	{
		gotoxy(weiyi, i);
		printf(qiangbi);

		gotoxy(weiyi + chang, i);
		printf(qiangbi);
	}

	gotoxy(1, 0);
	printf("分数:%d",snake.now_record);

	gotoxy(1, 0);

	snakemode();
}

食物模块

生成食物时横坐标的值笔者在全局变量中进行了列举,方便随机产生偶数,可以在果子被吃后迅速产生下一个果子。若是采用判断循环的方式生成果子坐标,蛇越长,果子越难以产生。

void foodmode()//生成果子
{
	srand(time(NULL));
	int creat_one = 1;//确认产生果子的需要
	food.x =hengzuobiao[ rand() % 33+0];
	food.y = rand() % 26+2;

	
		if (food.x % 2 == 0)
		{
			for (i = 0; i < snake.changdu; i++)
			{
				if (snake.x[i] == food.x && snake.y[i] == food.y)//确认坐标不在蛇体内
				{
					creat_one = 0;
				}
				if (creat_one)//生成果子
				{
					gotoxy(food.x, food.y);
					printf(guozi);

					need = 0;//不需要在下一循环生成果子

					gotoxy(0, 0);
					
				}
			}
		}
	
}

蛇模块

在游戏地图中央生成初始蛇

void snakemode()//产生初始蛇
{
	snake.changdu = height;
	snake.x[0] = chang / 2 + weiyi;
	snake.y[0] = kuan / 2;

	gotoxy(snake.x[0], snake.y[0]);//生成蛇头
	printf(head);

	for (i = 1; i < snake.changdu; i++)//循环生成蛇身
	{
		snake.x[i] = snake.x[i - 1] + 2;
		snake.y[i] = snake.y[i - 1];

		gotoxy(snake.x[i], snake.y[i]);
		printf(body);
	}

	gotoxy(0, 0);
}

移动模块(包含暂停功能)

注意x,y的移动单位是不一样的

void movemode()//移动模块
{
     char shangci;
	 shangci = direct;
	if (_kbhit())
	{
		fflush(stdin);
		direct = _getch();
		if (direct != 'w' && direct != 's' && direct != 'd' && direct != 'a'&&direct != 'W' && direct != 'S' && direct != 'D' && direct != 'A' && direct!=32)
		{
			direct = shangci;
		}
	}

	gotoxy(snake.x[snake.changdu - 1], snake.y[snake.changdu - 1]);//覆盖蛇尾
	printf("  ");

	for (i = snake.changdu - 1; i > 0; i--)//循环生成蛇身
	{
		snake.x[i] = snake.x[i - 1];
		snake.y[i] = snake.y[i - 1];

		gotoxy(snake.x[i], snake.y[i]);
		printf(body);
	}
	
	first:
	switch (direct)//确认蛇的移动方向
	{
	case 'w':
	case 'W':
		snake.y[0]--;
		break;
	case 's':
	case 'S':
		snake.y[0]++;
		break;
	case 'd':
	case 'D':
		snake.x[0] += 2;
		break;
	case 'a':
	case 'A':
		snake.x[0] -= 2;
		break;
	case 32:
		
		char xuanze;
		gotoxy(95, 10);
		printf("①继续游戏");
		gotoxy(95, 11);
		printf("②退出游戏");
		xuanze = _getch();
		system("pause>nul");
		if (xuanze =='1' )
		{
			gotoxy(95, 10);
			printf("            ");
			gotoxy(95, 11);
			printf("             ");
			direct = shangci;
			goto first;
		}
		else if(xuanze=='2')
		{
			system("cls");
			gotoxy(chang / 2 + weiyi, kuan / 2);
			Sleep(500);
			return;
		}
	}

	gotoxy(snake.x[0], snake.y[0]);//生成蛇头
	printf(head);

	gotoxy(0, 0);

	if (snake.x[0] == food.x && snake.y[0] == food.y)//判断是否吃到果子
	{
		snake.changdu++;
		snake.now_record++;
		gotoxy(1, 0);
		printf("        ");
		gotoxy(4, 0);
		printf("分数:%d",snake.now_record);
		need = 1;
	}
}

结束模块

判断蛇的位置,有两种情况

int endmode()//判断是否结束
{
	//撞墙
	if (snake.x[0] == weiyi + chang-1|| snake.x[0] == weiyi-1 || snake.y[0] == 0 || snake.y[0] == kuan - 1)
	{
		return 1;
	}
	//自杀
	for (i = 1; i <= snake.changdu; i++)
	{
		if (snake.x[i] == snake.x[0] && snake.y[i] == snake.y[0])
		{
			return 1;
		}
	}

	return 0;
}

主程序

int main()//主程序
{
    HideCursor();
	menu();

	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42098648/article/details/127554350