手把手教你写贪吃蛇(C语言)

目录

 

前言:程序包下载

一、涉及C语言知识点

二、贪吃蛇相关库函数及其头文件简介:

1、#include

(1)void fflush(stdin)

(2)CONSOLE_CURSOR_INFO

(3)GetConsoleCursorInfo

(4)SetConsoleCursorInfo

2、#include

(1)rand()

(2)void srand(unsigned int seed);

3、#include

(1)int kbhit(void);

(2)int getch(void)

4、#include

(1)HANDLE GetStdHandle( DWORD nStdHandle );

(2)COORD coord={x,y};

(3)SetConsoleCursorPosition(HANDLE ,coord); 

5、#include

(1)time_t time(time_t * timer)

三、贪吃蛇相关子函数

1、光标移动到 (X,Y)

2、画墙体、蛇、食物 

3、按键操作

4、食物刷新

5、蛇的状态

6、开始游戏

7、难度选择

8、主菜单

9、主函数

四、项目代码


前言:程序包下载

下载方式一:CSDN下载:https://download.csdn.net/download/qq_38351824/12142282

下载方式二:微信公众号 ① 关注Tech云   ② 暂不提供下载链接

下载方式三:文章下评论

一、涉及C语言知识点

数组、结构体、全局变量、局部变量、宏定义、条件语句、循环语句

二、贪吃蛇相关库函数及其头文件简介:

1、#include<stdio.h>

(1)void fflush(stdin)

① 简介

      清空输入缓冲区,通常是为了确保不影响后面的数据读取(例如在读完一个字符串后紧接着又要读取一个字符,此时应该先执行 fflush(stdin);

② 语法

int fflush(FILE *stream)

(2)CONSOLE_CURSOR_INFO

① 简介

        包含有关控制台光标的信息。

② 语法

typedef struct _CONSOLE_CURSOR_INFO {
  DWORD dwSize;
  BOOL  bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;

③ 参数

       dwSize:光标填充的字符单元格的百分比。该值介于1和100之间。光标外观会发生变化,从完全填充单元格到显示为单元格底部的水平线。

       bVisible:光标的可见性。如果光标可见,则此成员为TRUE

(3)GetConsoleCursorInfo

① 简介

        获取有关指定控制台屏幕缓冲区的光标大小和可见性的信息。

② 语法

BOOL WINAPI GetConsoleCursorInfo(
  _In_  HANDLE               hConsoleOutput,
  _Out_ PCONSOLE_CURSOR_INFO lpConsoleCursorInfo
);

③ 参数

参数:

  • hConsoleOutput 控制台屏幕缓冲区的句柄。句柄必须具有GENERIC_READ访问权限。
  • lpConsoleCursorInfo 指向CONSOLE_CURSOR_INFO结构的指针,该结构接收有关控制台游标的信息。

返回值:

  • 如果函数成功,则返回值为非零值。
  • 如果函数失败,则返回值为零。

(4)SetConsoleCursorInfo

① 简介

设置指定控制台屏幕缓冲区的光标大小和可见性。

② 语法

BOOL WINAPI SetConsoleCursorInfo(
  _In_       HANDLE              hConsoleOutput,
  _In_ const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
);

③ 参数

参数:

  • hConsoleOutput 控制台屏幕缓冲区的句柄。句柄必须具有GENERIC_READ访问权限。
  • lpConsoleCursorInfo 指向CONSOLE_CURSOR_INFO结构的指针,该结构为控制台屏幕缓冲区的游标提供新规范。

返回值:

  • 如果函数成功,则返回值为非零值。
  • 如果函数失败,则返回值为零。

2、#include<stdlib.h>

(1)rand()

① 简介

        rand()函数产生随机数的一个随机函数。

② 语法

rand()

③ 参数

若要生成 a 与 b 之间的随机实数,应使用: RAND()*(b-a)+a

(2)void srand(unsigned int seed);

① 简介

        srand函数是随机数发生器的初始化函数。

② 语法

void srand(unsigned int seed);

③ 参数

unsigned seed:随机数产生器的初始值(种子值)。

3、#include<conio.h>

(1)int kbhit(void);

① 简介

用于非阻塞地响应键盘输入事件。

② 语法

int kbhit(void);

③ 参数

功能及返回值: 检查当前是否有键盘输入,若有则返回一个非0值,否则返回0。

(2)int getch(void)

① 简介

一个不回显函数,当用户按下某个字符时,函数自动读取,无需按回车,有的C语言命令行程序会用到此函数做游戏

② 语法

int getch(void)

③ 参数

返回值:读取的字符

4、#include<windows.h>

(1)HANDLE GetStdHandle( DWORD nStdHandle );

① 简介

        GetStdHandle是一个Windows API函数。它用于从一个特定的标准设备(标准输入、标准输出或标准错误)中取得一个句柄(用来标识不同设备的数值)。

② 语法

HANDLE GetStdHandle( DWORD nStdHandle );

GetStdHandle()返回标准的输入、输出或错误的设备的句柄,也就是获得输入、输出/错误的屏幕缓冲区的句柄。

③ 参数

含义

STD_INPUT_HANDLE

标准输入的句柄

STD_OUTPUT_HANDLE

标准输出的句柄

STD_ERROR_HANDLE

标准错误的句柄

(2)COORD coord={x,y};

① 简介

        COORD是Windows API中定义的一种结构,表示一个字符在控制台屏幕上的坐标。

② 语法

typedef struct _COORD {

SHORT X;  水平坐标
SHORT Y;  垂直坐标

} COORD;

③ 参数

SHORT 为一种数据类型,范围-32768~+32767。

SHORT X;         水平坐标
SHORT Y;         垂直坐标

(3)SetConsoleCursorPosition(HANDLE ,coord); 

① 简介

        SetConsoleCursorPosition是API中定位光标位置的函数。

② 语法

 SetConsoleCursorPosition(HANDLE ,coord);         设置控制台光标坐标(设备句柄, 光标坐标) 

③ 参数

使用这个函数需要两个参数:第一个参数类型为HANDLE,第二个参数类型为COORD

5、#include<time.h>

(1)time_t time(time_t * timer)

① 简介

        获取当前的系统时间,返回的结果是一个time_t类型

② 语法

time_t time(time_t * timer)

③ 参数

获取当前时间:NULL。

返回的结果是一个time_t类型,其实就是一个大整数。

三、贪吃蛇相关子函数

1、光标移动到 (X,Y)

光标移动 到 (X,Y)
void gotoxy(int x, int y) 
{	
    COORD coord={x,y};                                  初始化结构体coord (坐标系X,Y)
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);    获取输出屏幕缓冲区句柄
    SetConsoleCursorPosition(handle, coord);            移动光标
}

2、画墙体、蛇、食物 

//画墙体、蛇、食物 
//卍  ※ 代表墙体
//♂     代表蛇体
//♀     代表食物
void drawMap(void)
{
	//画 --- 游戏说明	
	gotoxy(100, 7);
	printf("按键说明:");
	gotoxy(100, 10);
	printf("W:向上");
	gotoxy(100, 13);
	printf("A:向左");
	gotoxy(100, 16);
	printf("S:向下");
	gotoxy(100, 19);
	printf("D:向右");
	gotoxy(100,22);
	printf("当前分数:%d",score);
	
	//画 --- 贪吃蛇墙体 
	  // 画左右两面墙  
	for (int i = 0; i <= MAP_HIGHTH; i++) {  
		gotoxy(0,i);
		printf("※");
		gotoxy(MAP_WIDTH,i);
		printf("※");
		Sleep(20);
	}
	  // 画上下两面墙
	for (int i = 0; i <= MAP_WIDTH; i += 2) {
		gotoxy(i, 0);
		printf("卍");
		gotoxy(MAP_WIDTH-i, MAP_HIGHTH);
		printf("卍");
		Sleep(20);
	}
	
	//画 --- 蛇身 
	snake.len = SNAKE_BEGIN_LEN;  // 赋值刚开始的蛇身长度 
	srand((unsigned)time(NULL));  // 防止伪随机数 
	do {
		snake.x[0] = rand() % (MAP_WIDTH/2 ) + MAP_WIDTH/4;
		snake.y[0] = rand() % (MAP_HIGHTH/2 ) + MAP_HIGHTH/4;
	} while (snake.x[0] % 2 != 0); // 随机出现蛇头,保证蛇头在地图中间范围内,而不在墙体上 ,并且符合蛇是偶数的要求 
	
	gotoxy(snake.x[0], snake.y[0]);// 光标移动到蛇头的位置 
	printf("♂");                  // 打印蛇头 
	for (int i = 1; i < snake.len; i++) {// 打印蛇身 
		snake.x[i] = snake.x[0];
		snake.y[i] = snake.y[0] + 1;
		gotoxy(snake.x[i], snake.y[i]);
		printf("♂");
	}
	do {
		food.x = rand() % (MAP_WIDTH - 4) + 2;
		food.y = rand() % (MAP_HIGHTH - 2) + 1;
	} while (food.x % 2 != 0); // 随机出现食物,保证食物不在墙体上,并且符合食物是偶数的要求 
	gotoxy(food.x, food.y);    // 光标移动到食物的位置 
	printf("♀");              // 打印食物 
}

3、按键操作

//按键操作
void keyDown(void) 
{
	//按键的处理
	if (_kbhit()) // 检查当前是否有键盘输入 
	{
			fflush(stdin);  // 清空输入缓冲区,读完一个字符串后紧接着又要读取一个字符,此时应该先执行该函数 
			key = _getch(); // 一个不回显函数,当用户按下某个字符时,函数自动读取,无需按回车
			
			//这一大段代码是为了防止回头自杀的情况发生,如果用户输入反方向或者非wasd键,key的值不会改变
			if (key != 'w' && key != 'W' && key != 's' && key != 'S' && key != 'A' && key != 'a' && key != 'd' && key != 'D')
				key = tempkey;    // 如果用户输入非wasd键,key的值不会改变
			else {                // 如果用户输入反方向,key的值不会改变
				if (tempkey == 'w' || tempkey == 'W')
					if (key == 's' || key == 'S')
						key = tempkey;
				if (tempkey == 's' || tempkey == 'S')
					if (key == 'w' || key == 'W')                                          
						key = tempkey;
				if (tempkey == 'a' || tempkey == 'A')
					if (key == 'D' || key == 'd')
						key = tempkey;
				if (tempkey == 'D' || tempkey == 'd')
					if (key == 'a' || key == 'A')
						key = tempkey;
				tempkey = key;
			}
	}
	if (!changeflag)// 如果蛇没吃东西,蛇尾就空了 ,擦除尾巴 
	{
		gotoxy(snake.x[snake.len - 1], snake.y[snake.len - 1]);
		printf("  ");
	}
	else {   // 蛇吃了一个食物,变长了 
		snake.x[snake.len] = snake.x[snake.len - 1];
		snake.y[snake.len] = snake.y[snake.len - 1];
		snake.len++;
	}
	for (int i = snake.len-1; i>0; i--) { // 后面一个的蛇身,替换前面的一个 ,蛇头变成食物的位置 
		snake.x[i] = snake.x[i - 1];
		snake.y[i] = snake.y[i - 1];
	}
	switch (key) {// 判断按键数据 
	case 'w':
	case 'W':
		snake.y[0]--;
		break;
	case 's':
	case 'S':
		snake.y[0]++;
		break;
	case 'a':
	case 'A':
		snake.x[0] -= 2;
		break;
	case 'd':
	case 'D':
		snake.x[0] += 2;
		break;
	}
	gotoxy(snake.x[0], snake.y[0]); // 改变头的方向 
	printf("♂");
	changeflag =false;   // 清空标志位 
}

4、食物刷新

//食物的刷新
void createFood(void) 
{
	if (snake.x[0] == food.x&&snake.y[0] == food.y) {// 如果食物被吃了 
		score++;                                     // 分数 +1 
		gotoxy(100, 22);
		printf("当前分数:%d", score);
		srand((unsigned)time(NULL));
		while (1)
		{
			bool flag = true;
			food.x = rand() % (MAP_WIDTH - 4) + 2;   // 随机产生食物 ,不在墙体上 
			food.y = rand() % (MAP_HIGHTH - 2) + 1;
			for (int k = 0; k < snake.len; k++)      // 判断食物不在贪吃蛇身上
			{
				if (snake.x[k] == food.x&&snake.y[k] == food.y)
				{
					flag = false;  
					break;
				}
			}
			if (flag&&food.x % 2 == 0)// 食物合不合适标记 --- 蛇吃不到 
				break;
		}
		gotoxy(food.x, food.y);
		printf("♂");
		changeflag = true;  // 吃了食物,表示蛇长度有变化,标志位 
	}
}

5、蛇的状态

//蛇的状态
bool snakeStatus(void) 
{
	if (snake.x[0] == 0 || snake.x[0] == MAP_WIDTH  || snake.y[0] == 0 || snake.y[0] == MAP_HIGHTH)
		return false;  // 如果蛇在墙上,宣布死亡 
	for (int i = 1; i < snake.len; i++)
		if (snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i])
			return false;// 如果蛇自己吃了自己,宣布死亡
	return true;
}

6、开始游戏

void playGame(void)
{
	key = 'w';
	score = 0;
	//隐藏光标
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 获取输出屏幕缓冲区句柄
	CONSOLE_CURSOR_INFO cci;   // 初始化结构体cci 
	GetConsoleCursorInfo(hOut, &cci); // 获取有关指定控制台屏幕缓冲区的光标大小和可见性的信息。
	cci.bVisible = FALSE;  // 光标的不可见
	SetConsoleCursorInfo(hOut, &cci); //设置指定控制台屏幕缓冲区的光标大小和可见性。
	system("CLS");
	
	drawMap();  //画墙体、蛇、食物 
	while (1) {
		keyDown();//按键操作
		Sleep(snake.speed); // 蛇的速度设定 
		createFood();//食物的刷新
		if (!snakeStatus())//蛇的状态
			break;
	}
	system("CLS");
	gotoxy(50, 8);
	printf("——游戏结束——");
	gotoxy(52, 10);
	printf("你的分数为%d", score);
	gotoxy(53, 13);
	printf("再来一次");
	gotoxy(53, 16);
	printf("退出游戏");
	gotoxy(53, 19);
	printf("回主菜单");
	while (1) {
		while (1) {
			if (_kbhit()) {// 检查当前是否有键盘输入 
				key3 = _getch();// 获取键值 
				if (0 == key3 || 0xE0 == key3)
				    key3 = key3 << 8 | _getch();
			}
			else break;
		} 
		switch (flag3) {
		case 0:
			gotoxy(51, 13);
			printf("→");
			gotoxy(51, 16);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			key3 = _getch();// 获取键值 
			if (key3 == 13) // 确定 
				playGame(); // 开始游戏 
			break;
		case 1:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 16);
			printf("→");
			gotoxy(51, 19);
			printf("  ");
			key3 = _getch();// 获取键值 
			if (key3 == 13) // 确定 
				exit(0);    // 退出 
			break;
		case 2:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 16);
			printf("  ");
			gotoxy(51, 19);
			printf("→");
			key3 = _getch(); // 获取键值  
			if (key3 == 13)  // 确定 
				main(); // 回到主菜单 
			break;
		}
		switch (key3) { // 获取键值 
		case 's':
		case 'S':
			if (flag3 < 2)
				flag3++;
			break;
		case 'W':
		case 'w':
			if (flag3 > 0)
				flag3--;
			break;
		}
	}
}

7、难度选择

void settingMeau(void)
{
	system("CLS");
	gotoxy(50, 8);
	printf("——游戏难度——");
	gotoxy(53, 13);
	printf("地狱");
	gotoxy(53, 15);
	printf("狂暴");
	gotoxy(53, 17);
	printf("正常");
	gotoxy(53, 19);
	printf("平和");
	gotoxy(53, 23);
	printf("返回菜单");
	switch (setflag) {
	case 0:
		gotoxy(58, 13);
		printf("←");
		break;
	case 1:
		gotoxy(58, 15);
		printf("←");
		break;
	case 2:
		gotoxy(58, 17);
		printf("←");
		break;
	case 3:
		gotoxy(58, 19);
		printf("←");
		break;
	}
	while (1) {
		switch (flag2) {
		case 0:
			gotoxy(51, 13);
			printf("→");
			gotoxy(51, 15);
			printf("  ");
			gotoxy(51, 17);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			gotoxy(52, 23);
			printf("  ");
			key4 = _getch();
			if (key4 == 13) {
				snake.speed = 60;
				setflag = 0;
				gotoxy(58, 13);
				printf("→");
				gotoxy(58, 15);
				printf("  ");
				gotoxy(58, 17);
				printf("  ");
				gotoxy(58, 19);
				printf("  ");
			}
			break;
		case 1:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 15);
			printf("→");
			gotoxy(51, 17);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			gotoxy(52, 23);
			printf("  ");
			key4 = _getch();
			if (key4 == 13) {
				snake.speed = 100;
				setflag = 1;
				gotoxy(58, 13);
				printf("  ");
				gotoxy(58, 15);
				printf("→");
				gotoxy(58, 17);
				printf("  ");
				gotoxy(58, 19);
				printf("  ");
			}
			break;
		case 2:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 15);
			printf("  ");
			gotoxy(51, 17);
			printf("→");
			gotoxy(51, 19);
			printf("  ");
			gotoxy(52, 23);
			printf("  ");
			key4 = _getch();
			if (key4 == 13) {
				snake.speed = 200;
				setflag = 2;
				gotoxy(58, 13);
				printf("  ");
				gotoxy(58, 15);
				printf("  ");
				gotoxy(58, 17);
				printf("→");
				gotoxy(58, 19);
				printf("  ");
			}
			break;
		case 3:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 15);
			printf("  ");
			gotoxy(51, 17);
			printf("  ");
			gotoxy(51, 19);
			printf("→");
			gotoxy(52, 23);
			printf("  ");
			key4 = _getch();
			if (key4 == 13) {
				snake.speed = 300;
				setflag = 3;
				gotoxy(58, 13);
				printf("  ");
				gotoxy(58, 15);
				printf("  ");
				gotoxy(58, 17);
				printf("  ");
				gotoxy(58, 19);
				printf("→");
			}
			break;
		case 4:gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 15);
			printf("  ");
			gotoxy(51, 17);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			gotoxy(52, 23);
			printf("→");
			key4 = _getch();// 获取键值 
			if (key4 == 13) // 确定 
				main();     // 回到主菜单
		}
		switch (key4) { // 获取键值 
		case 's':
		case 'S':
			if (flag2 < 4)
				flag2++;
			break;
		case 'W':
		case 'w':
			if (flag2 > 0)
				flag2--;
			break;
		}
	}
}

8、主菜单

void meau(void)
{
	//隐藏光标
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 获取输出屏幕缓冲区句柄
	CONSOLE_CURSOR_INFO cci;   // 初始化结构体cci 
	GetConsoleCursorInfo(hOut, &cci); // 获取有关指定控制台屏幕缓冲区的光标大小和可见性的信息。
	cci.bVisible = FALSE;  // 光标的不可见
	SetConsoleCursorInfo(hOut, &cci); //设置指定控制台屏幕缓冲区的光标大小和可见性。
	
	system("CLS"); // 调用系统命令cls完成清屏操作
	gotoxy(50, 8);
	printf("——贪吃蛇——");
	gotoxy(53, 13);
	printf("开始游戏");
	gotoxy(53, 16);
	printf("游戏难度");
	gotoxy(53, 19);
	printf("退出游戏");
	
	gotoxy(44, 22);
	printf("※ ※ CSDN:Sumjess ※ ※");
	gotoxy(43, 25);
	printf("※ ※ 公众号 :Tech云 ※ ※");	
			
	while (1) {
		switch (flag) {
		case 0:
			gotoxy(51, 13);
			printf("→");
			gotoxy(51, 16);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			key2 = _getch();// 获取键值 
			if (key2 == 13) // 如果是确定 
				playGame(); // 进入游戏 
			break;
		case 1:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 16);
			printf("→");
			gotoxy(51, 19);
			printf("  ");
			key2 = _getch();// 获取键值 
			if (key2 == 13) // 如果是确定 
			  settingMeau();// 进入难度设定 
			break;
		case 2:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 16);
			printf("  ");
			gotoxy(51, 19);
			printf("→");
			key2 = _getch();// 获取键值 
			if (key2 == 13) // 如果是确定 
				exit(0);    // 退出 
			break;
		}
		switch (key2) {// 获取键值 
		case 's':
		case 'S':
			if (flag < 2)
				flag++;
			break;
		case 'W':
		case 'w':
			if (flag > 0)
				flag--;
			break;
		}
	}
}

9、主函数

int main(void)
{
	//隐藏光标
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 获取输出屏幕缓冲区句柄
	CONSOLE_CURSOR_INFO cci;   // 初始化结构体cci 
	GetConsoleCursorInfo(hOut, &cci); // 获取有关指定控制台屏幕缓冲区的光标大小和可见性的信息。
	cci.bVisible = FALSE;  // 光标的不可见
	SetConsoleCursorInfo(hOut, &cci); //设置指定控制台屏幕缓冲区的光标大小和可见性。
	meau();
}

四、项目代码

#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<windows.h>
#include<time.h>


//辅助宏定义
#define GAME_H
#define MAP_HIGHTH 29            //地图高度
#define MAP_WIDTH 90              //地图宽度 
#define SNAKE_MAX_LEN 1000         //蛇的最大长度    
#define SNAKE_BEGIN_LEN 3         //蛇的初始长度
//数据设计 
//蛇结构体
struct
{
	int x[SNAKE_MAX_LEN];
	int y[SNAKE_MAX_LEN];
	int len;                      
	int speed=150;
}snake;
//食物结构体
struct
{
	int x;
	int y;
}food;
//其他全局变量
char key = 'w'; 
char key2;                    //key2代表在初始菜单界面的键盘输入
char key3;                    //key3代表在游戏结束后菜单的键盘输入
char key4;                    //key4代表在游戏难度内的菜单的键盘输入
int tempkey = 'w';            //这里的tempkey是为了防止贪吃蛇回头自杀而设定的,详细见keyDown()函数
bool changeflag = 0; 
int flag = 0;                 //这三个flag是为了反映按下w与s键后的状态,详细请看下面代码
int flag2 = 0;
int flag3 = 0;
int setflag = 2;              //这个setflag是为了记录游戏难度里选择难度后再难度后面打印◇     
int score = 0;                //记录分数
//函数模块设计
//画地图
void drawMap(void);
//按键操作
void keyDown(void);
//食物的刷新
void createFood(void);
//蛇的状态
bool snakeStatus(void);
//光标移动
void gotoxy(int x, int y);

void playGame(void);
void meau(void);
void settingMeau(void);
int main(void)
{
	//隐藏光标
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 获取输出屏幕缓冲区句柄
	CONSOLE_CURSOR_INFO cci;   // 初始化结构体cci 
	GetConsoleCursorInfo(hOut, &cci); // 获取有关指定控制台屏幕缓冲区的光标大小和可见性的信息。
	cci.bVisible = FALSE;  // 光标的不可见
	SetConsoleCursorInfo(hOut, &cci); //设置指定控制台屏幕缓冲区的光标大小和可见性。
	meau();    // 主菜单 
}

void meau(void)
{
	//隐藏光标
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 获取输出屏幕缓冲区句柄
	CONSOLE_CURSOR_INFO cci;   // 初始化结构体cci 
	GetConsoleCursorInfo(hOut, &cci); // 获取有关指定控制台屏幕缓冲区的光标大小和可见性的信息。
	cci.bVisible = FALSE;  // 光标的不可见
	SetConsoleCursorInfo(hOut, &cci); //设置指定控制台屏幕缓冲区的光标大小和可见性。
	
	system("CLS"); // 调用系统命令cls完成清屏操作
	gotoxy(50, 8);
	printf("——贪吃蛇——");
	gotoxy(53, 13);
	printf("开始游戏");
	gotoxy(53, 16);
	printf("游戏难度");
	gotoxy(53, 19);
	printf("退出游戏");
	
	gotoxy(44, 22);
	printf("※ ※ CSDN:Sumjess ※ ※");
	gotoxy(43, 25);
	printf("※ ※ 公众号 :Tech云 ※ ※");	
			
	while (1) {
		switch (flag) {
		case 0:
			gotoxy(51, 13);
			printf("→");
			gotoxy(51, 16);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			key2 = _getch();// 获取键值 
			if (key2 == 13) // 如果是确定 
				playGame(); // 进入游戏 
			break;
		case 1:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 16);
			printf("→");
			gotoxy(51, 19);
			printf("  ");
			key2 = _getch();// 获取键值 
			if (key2 == 13) // 如果是确定 
			  settingMeau();// 进入难度设定 
			break;
		case 2:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 16);
			printf("  ");
			gotoxy(51, 19);
			printf("→");
			key2 = _getch();// 获取键值 
			if (key2 == 13) // 如果是确定 
				exit(0);    // 退出 
			break;
		}
		switch (key2) {// 获取键值 
		case 's':
		case 'S':
			if (flag < 2)
				flag++;
			break;
		case 'W':
		case 'w':
			if (flag > 0)
				flag--;
			break;
		}
	}
}
void settingMeau(void)
{
	system("CLS");
	gotoxy(50, 8);
	printf("——游戏难度——");
	gotoxy(53, 13);
	printf("地狱");
	gotoxy(53, 15);
	printf("狂暴");
	gotoxy(53, 17);
	printf("正常");
	gotoxy(53, 19);
	printf("平和");
	gotoxy(53, 23);
	printf("返回菜单");
	switch (setflag) {
	case 0:
		gotoxy(58, 13);
		printf("←");
		break;
	case 1:
		gotoxy(58, 15);
		printf("←");
		break;
	case 2:
		gotoxy(58, 17);
		printf("←");
		break;
	case 3:
		gotoxy(58, 19);
		printf("←");
		break;
	}
	while (1) {
		switch (flag2) {
		case 0:
			gotoxy(51, 13);
			printf("→");
			gotoxy(51, 15);
			printf("  ");
			gotoxy(51, 17);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			gotoxy(52, 23);
			printf("  ");
			key4 = _getch();
			if (key4 == 13) {
				snake.speed = 60;
				setflag = 0;
				gotoxy(58, 13);
				printf("→");
				gotoxy(58, 15);
				printf("  ");
				gotoxy(58, 17);
				printf("  ");
				gotoxy(58, 19);
				printf("  ");
			}
			break;
		case 1:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 15);
			printf("→");
			gotoxy(51, 17);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			gotoxy(52, 23);
			printf("  ");
			key4 = _getch();
			if (key4 == 13) {
				snake.speed = 100;
				setflag = 1;
				gotoxy(58, 13);
				printf("  ");
				gotoxy(58, 15);
				printf("→");
				gotoxy(58, 17);
				printf("  ");
				gotoxy(58, 19);
				printf("  ");
			}
			break;
		case 2:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 15);
			printf("  ");
			gotoxy(51, 17);
			printf("→");
			gotoxy(51, 19);
			printf("  ");
			gotoxy(52, 23);
			printf("  ");
			key4 = _getch();
			if (key4 == 13) {
				snake.speed = 200;
				setflag = 2;
				gotoxy(58, 13);
				printf("  ");
				gotoxy(58, 15);
				printf("  ");
				gotoxy(58, 17);
				printf("→");
				gotoxy(58, 19);
				printf("  ");
			}
			break;
		case 3:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 15);
			printf("  ");
			gotoxy(51, 17);
			printf("  ");
			gotoxy(51, 19);
			printf("→");
			gotoxy(52, 23);
			printf("  ");
			key4 = _getch();
			if (key4 == 13) {
				snake.speed = 300;
				setflag = 3;
				gotoxy(58, 13);
				printf("  ");
				gotoxy(58, 15);
				printf("  ");
				gotoxy(58, 17);
				printf("  ");
				gotoxy(58, 19);
				printf("→");
			}
			break;
		case 4:gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 15);
			printf("  ");
			gotoxy(51, 17);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			gotoxy(52, 23);
			printf("→");
			key4 = _getch();// 获取键值 
			if (key4 == 13) // 确定 
				main();     // 回到主菜单
		}
		switch (key4) { // 获取键值 
		case 's':
		case 'S':
			if (flag2 < 4)
				flag2++;
			break;
		case 'W':
		case 'w':
			if (flag2 > 0)
				flag2--;
			break;
		}
	}
}
void playGame(void)
{
	key = 'w';
	score = 0;
	//隐藏光标
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 获取输出屏幕缓冲区句柄
	CONSOLE_CURSOR_INFO cci;   // 初始化结构体cci 
	GetConsoleCursorInfo(hOut, &cci); // 获取有关指定控制台屏幕缓冲区的光标大小和可见性的信息。
	cci.bVisible = FALSE;  // 光标的不可见
	SetConsoleCursorInfo(hOut, &cci); //设置指定控制台屏幕缓冲区的光标大小和可见性。
	system("CLS");
	
	drawMap();  //画墙体、蛇、食物 
	while (1) {
		keyDown();//按键操作
		Sleep(snake.speed); // 蛇的速度设定 
		createFood();//食物的刷新
		if (!snakeStatus())//蛇的状态
			break;
	}
	system("CLS");
	gotoxy(50, 8);
	printf("——游戏结束——");
	gotoxy(52, 10);
	printf("你的分数为%d", score);
	gotoxy(53, 13);
	printf("再来一次");
	gotoxy(53, 16);
	printf("退出游戏");
	gotoxy(53, 19);
	printf("回主菜单");
	while (1) {
		while (1) {
			if (_kbhit()) {// 检查当前是否有键盘输入 
				key3 = _getch();// 获取键值 
				if (0 == key3 || 0xE0 == key3)
				    key3 = key3 << 8 | _getch();
			}
			else break;
		} 
		switch (flag3) {
		case 0:
			gotoxy(51, 13);
			printf("→");
			gotoxy(51, 16);
			printf("  ");
			gotoxy(51, 19);
			printf("  ");
			key3 = _getch();// 获取键值 
			if (key3 == 13) // 确定 
				playGame(); // 开始游戏 
			break;
		case 1:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 16);
			printf("→");
			gotoxy(51, 19);
			printf("  ");
			key3 = _getch();// 获取键值 
			if (key3 == 13) // 确定 
				exit(0);    // 退出 
			break;
		case 2:
			gotoxy(51, 13);
			printf("  ");
			gotoxy(51, 16);
			printf("  ");
			gotoxy(51, 19);
			printf("→");
			key3 = _getch(); // 获取键值  
			if (key3 == 13)  // 确定 
				main(); // 回到主菜单 
			break;
		}
		switch (key3) { // 获取键值 
		case 's':
		case 'S':
			if (flag3 < 2)
				flag3++;
			break;
		case 'W':
		case 'w':
			if (flag3 > 0)
				flag3--;
			break;
		}
	}
}
//画墙体、蛇、食物 
//卍  ※ 代表墙体
//♂     代表蛇体
//♀     代表食物
void drawMap(void)
{
	//画 --- 游戏说明	
	gotoxy(100, 7);
	printf("按键说明:");
	gotoxy(100, 10);
	printf("W:向上");
	gotoxy(100, 13);
	printf("A:向左");
	gotoxy(100, 16);
	printf("S:向下");
	gotoxy(100, 19);
	printf("D:向右");
	gotoxy(100,22);
	printf("当前分数:%d",score);
	
	gotoxy(98, 25);
	printf("※CSDN:Sumjess ※");
	gotoxy(97, 28);
	printf("※公众号 :Tech云 ※");	
	
	//画 --- 贪吃蛇墙体 
	  // 画左右两面墙  
	for (int i = 0; i <= MAP_HIGHTH; i++) {  
		gotoxy(0,i);
		printf("※");
		gotoxy(MAP_WIDTH,i);
		printf("※");
		Sleep(20);
	}
	  // 画上下两面墙
	for (int i = 0; i <= MAP_WIDTH; i += 2) {
		gotoxy(i, 0);
		printf("卍");
		gotoxy(MAP_WIDTH-i, MAP_HIGHTH);
		printf("卍");
		Sleep(20);
	}
	
	//画 --- 蛇身 
	snake.len = SNAKE_BEGIN_LEN;  // 赋值刚开始的蛇身长度 
	srand((unsigned)time(NULL));  // 防止伪随机数 
	do {
		snake.x[0] = rand() % (MAP_WIDTH/2 ) + MAP_WIDTH/4;
		snake.y[0] = rand() % (MAP_HIGHTH/2 ) + MAP_HIGHTH/4;
	} while (snake.x[0] % 2 != 0); // 随机出现蛇头,保证蛇头在地图中间范围内,而不在墙体上 ,并且符合蛇是偶数的要求 
	
	gotoxy(snake.x[0], snake.y[0]);// 光标移动到蛇头的位置 
	printf("♂");                  // 打印蛇头 
	for (int i = 1; i < snake.len; i++) {// 打印蛇身 
		snake.x[i] = snake.x[0];
		snake.y[i] = snake.y[0] + 1;
		gotoxy(snake.x[i], snake.y[i]);
		printf("♂");
	}
	do {
		food.x = rand() % (MAP_WIDTH - 4) + 2;
		food.y = rand() % (MAP_HIGHTH - 2) + 1;
	} while (food.x % 2 != 0); // 随机出现食物,保证食物不在墙体上,并且符合食物是偶数的要求 
	gotoxy(food.x, food.y);    // 光标移动到食物的位置 
	printf("♀");              // 打印食物 
}
//按键操作
void keyDown(void) 
{
	//按键的处理
	if (_kbhit()) // 检查当前是否有键盘输入 
	{
			fflush(stdin);  // 清空输入缓冲区,读完一个字符串后紧接着又要读取一个字符,此时应该先执行该函数 
			key = _getch(); // 一个不回显函数,当用户按下某个字符时,函数自动读取,无需按回车
			
			//这一大段代码是为了防止回头自杀的情况发生,如果用户输入反方向或者非wasd键,key的值不会改变
			if (key != 'w' && key != 'W' && key != 's' && key != 'S' && key != 'A' && key != 'a' && key != 'd' && key != 'D')
				key = tempkey;    // 如果用户输入非wasd键,key的值不会改变
			else {                // 如果用户输入反方向,key的值不会改变
				if (tempkey == 'w' || tempkey == 'W')
					if (key == 's' || key == 'S')
						key = tempkey;
				if (tempkey == 's' || tempkey == 'S')
					if (key == 'w' || key == 'W')                                          
						key = tempkey;
				if (tempkey == 'a' || tempkey == 'A')
					if (key == 'D' || key == 'd')
						key = tempkey;
				if (tempkey == 'D' || tempkey == 'd')
					if (key == 'a' || key == 'A')
						key = tempkey;
				tempkey = key;
			}
	}
	if (!changeflag)// 如果蛇没吃东西,蛇尾就空了 ,擦除尾巴 
	{
		gotoxy(snake.x[snake.len - 1], snake.y[snake.len - 1]);
		printf("  ");
	}
	else {   // 蛇吃了一个食物,变长了 
		snake.x[snake.len] = snake.x[snake.len - 1];
		snake.y[snake.len] = snake.y[snake.len - 1];
		snake.len++;
	}
	for (int i = snake.len-1; i>0; i--) { // 后面一个的蛇身,替换前面的一个 ,蛇头变成食物的位置 
		snake.x[i] = snake.x[i - 1];
		snake.y[i] = snake.y[i - 1];
	}
	switch (key) {// 判断按键数据 
	case 'w':
	case 'W':
		snake.y[0]--;
		break;
	case 's':
	case 'S':
		snake.y[0]++;
		break;
	case 'a':
	case 'A':
		snake.x[0] -= 2;
		break;
	case 'd':
	case 'D':
		snake.x[0] += 2;
		break;
	}
	gotoxy(snake.x[0], snake.y[0]); // 改变头的方向 
	printf("♂");
	changeflag =false;   // 清空标志位 
}
//食物的刷新
void createFood(void) 
{
	if (snake.x[0] == food.x&&snake.y[0] == food.y) {// 如果食物被吃了 
		score++;                                     // 分数 +1 
		gotoxy(100, 22);
		printf("当前分数:%d", score);
		srand((unsigned)time(NULL));
		while (1)
		{
			bool flag = true;
			food.x = rand() % (MAP_WIDTH - 4) + 2;   // 随机产生食物 ,不在墙体上 
			food.y = rand() % (MAP_HIGHTH - 2) + 1;
			for (int k = 0; k < snake.len; k++)      // 判断食物不在贪吃蛇身上
			{
				if (snake.x[k] == food.x&&snake.y[k] == food.y)
				{
					flag = false;  
					break;
				}
			}
			if (flag&&food.x % 2 == 0)// 食物合不合适标记 --- 蛇吃不到 
				break;
		}
		gotoxy(food.x, food.y);
		printf("♂");
		changeflag = true;  // 吃了食物,表示蛇长度有变化,标志位 
	}
}
//蛇的状态
bool snakeStatus(void) 
{
	if (snake.x[0] == 0 || snake.x[0] == MAP_WIDTH  || snake.y[0] == 0 || snake.y[0] == MAP_HIGHTH)
		return false;  // 如果蛇在墙上,宣布死亡 
	for (int i = 1; i < snake.len; i++)
		if (snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i])
			return false;// 如果蛇自己吃了自己,宣布死亡
	return true;
}
//光标移动 到 (X,Y)
void gotoxy(int x, int y) 
{	
    COORD coord={x,y};                                  // 初始化结构体coord (坐标系X,Y)
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);    // 获取输出屏幕缓冲区句柄
    SetConsoleCursorPosition(handle, coord);            // 移动光标
}
发布了358 篇原创文章 · 获赞 693 · 访问量 40万+

猜你喜欢

转载自blog.csdn.net/qq_38351824/article/details/104202575