上海古都建筑设计集团,上海办公室装修设计公司,上海装修公司高质量的内容分享社区,上海装修公司我们不是内容生产者,我们只是上海办公室装修设计公司内容的搬运工平台

【贪吃蛇:C语言实现】

guduadmin261月前

文章目录

  • 前言
  • 1.了解Win32API相关知识
    • 1.1什么是Win32API
    • 1.2设置控制台的大小、名称
    • 1.3控制台上的光标
    • 1.4 GetStdHandle(获得控制台信息)
    • 1.5 SetConsoleCursorPosition(设置光标位置)
    • 1.6 GetConsoleCursorInfo(获得光标信息)
    • 1.7 SetConsoleCursorInfo(设置光标信息)
    • 1.8 GetAsyncKeyState(获取按键信息)
    • 2.游戏设计与分析
      • 2.1地图设计
      • 2.2数据结构的设计
        • 2.2.1蛇身的设计
        • 2.2.2蛇的方向
        • 2.2.3游戏的状态
        • 2.2.4管理整条蛇
        • 2.3.游戏流程
        • 3. 功能的具体实现
          • 游戏开始前
          • 3.1 初始化蛇
          • 3.2 创建食物
          • 游戏运行
          • 3.3 检测按键
          • 3.4 蛇移动
          • 3.5 判断节点是否是食物
          • 3.6 吃食物
          • 3.7 不吃食物
          • 3.8 撞墙
          • 3.9 撞到自己
          • 3.10 游戏结束
          • 3.11 main函数
          • 4.参考代码
            • 4.1 main
            • 4.2 Snake.h
            • 4.3 Snake.c

              【贪吃蛇:C语言实现】,在这里插入图片描述,第1张

              前言

              彻底学习完C语言之后,为巩固所学,我们来实现一个贪吃蛇小游戏。

              该游戏涉及到的C语言的知识有:函数、枚举、结构体、动态内存管理、预处理指令。

              另外,还包含数据结构(链表)以及Win32API相关知识。

              先看一下游戏效果吧

              【贪吃蛇:C语言实现】,在这里插入图片描述,第2张

              目前该游戏还有很多可以改进的地方,由于蛇的移动使用的是Sleep函数,看起来有点卡顿,而且按键会有一点延迟。

              代码仓库链接

              1.了解Win32API相关知识

              1.1什么是Win32API

              Win32 API也就是Microsoft Windows32位平台的应用程序编程接口。调用各种接口,可以帮应⽤程序达到开启视窗、描绘图形、使⽤周边设备等目的,由于这些函数服务的对象是应用程序(Application), 所以便称之为 Application Programming Interface,简称 API 函数。

              1.2设置控制台的大小、名称

              设置大小和名称的命令为:

              • mode con cols=列 lines=行
              • tiele 名称

                如果想在C语言中设置窗口,需要使用system函数。

                【贪吃蛇:C语言实现】,在这里插入图片描述,第3张

                1.3控制台上的光标

                COORD 是Windows API中定义的⼀个结构体,表示⼀个字符在控制台屏幕上的坐标,坐标系(0,0)的原点位于缓冲区的左侧顶部的单元格。使用需要window.h的头文件

                【贪吃蛇:C语言实现】,在这里插入图片描述,第4张

                COORD类型的声明:

                typedef struct _COORD {
                	SHORT X;//横坐标
                	SHORT Y;//纵坐标
                } COOR, *PCOORD;
                

                1.4 GetStdHandle(获得控制台信息)

                GetStdHandle是⼀个Windows API函数。它⽤于从⼀个特定的标准设备(标准输⼊、标准输出或标准错误)中取得⼀个句柄(⽤来标识不同设备的数值),使用这个句柄可以操作设备(控制台)。

                HANDLE WINAPI GetStdHandle( _In_ DWORD nStdHandle);
                

                该函数需要一个参数,参数如下:

                【贪吃蛇:C语言实现】,在这里插入图片描述,第5张

                1.5 SetConsoleCursorPosition(设置光标位置)

                设置指定控制台屏幕缓冲区中的光标位置,我们将想要设置的坐标信息放在COORD类型的pos中,调用SetConsoleCursorPosition函数将光标位置设置到指定的位置。

                BOOL WINAPI SetConsoleCursorPosition(
                 	HANDLE hConsoleOutput,     /要设置哪个控制台
                 	COORD pos		           /将光标设置的位置
                );
                

                通过上述三个功能,实现设置光标位置

                【贪吃蛇:C语言实现】,在这里插入图片描述,第6张

                1.6 GetConsoleCursorInfo(获得光标信息)

                GetConsoleCursorInfo:检索有关指定控制台屏幕缓冲区的光标大小可见性的信息。然后将获得的信息放在一个结构体中

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

                _ Out_ PCONSOLE_CURSOR_INFO 是指向 CONSOLE_CURSOR_INFO 结构体的指针

                该结构体,包含有关控制台光标的信息

                typedef struct _CONSOLE_CURSOR_INFO {
                 	DWORD dwSize;	
                 	BOOL bVisible;	
                } CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;
                
                • dwSize,由光标填充的字符单元格的百分比。 此值介于1到100之间。 光标外观会变化,范围从完全填充单元格到单元底部的⽔平线条。
                • bVisible,光标的可见性。 如果光标可见,则此成员为 TRUE。

                  【贪吃蛇:C语言实现】,在这里插入图片描述,第7张

                  1.7 SetConsoleCursorInfo(设置光标信息)

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

                  其参数和get方法相同

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

                  设置前:

                  【贪吃蛇:C语言实现】,在这里插入图片描述,第8张

                  设置后:

                  【贪吃蛇:C语言实现】,在这里插入图片描述,第9张

                  隐藏光标:

                  【贪吃蛇:C语言实现】,在这里插入图片描述,第10张

                  1.8 GetAsyncKeyState(获取按键信息)

                  GetAsyncKeyState:获取按键情况,GetAsyncKeyState的函数原型如下:

                  SHORT GetAsyncKeyState(
                   	int vKey
                  );
                  
                  • 将键盘上每个键的虚拟键值传递给函数,函数通过返回值来分辨按键的状态。了解虚拟键值点击这里
                  • GetAsyncKeyState 的返回值是short类型,在上⼀次调⽤ GetAsyncKeyState 函数后,如果返回的16位的short数据中,最高位是1,说明按键的状态是按下,如果最⾼是0,说明按键的状态是抬起;如果最低位被置为1则说明,该按键被按过,否则为0
                  • 如果我们要判断⼀个键是否被按过,可以检测GetAsyncKeyState返回值的最低值是否为1
                     #define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )
                    
                    #include
                    int main()
                    {
                    	while (1)
                    	{
                    		if (KEY_PRESS(0x30))
                    			printf("0\n");
                    		else if (KEY_PRESS(0x31))
                    			printf("1\n");
                    		else if (KEY_PRESS(0x32))
                    			printf("2\n");
                    		else if (KEY_PRESS(0x33))
                    			printf("3\n");
                    		else if (KEY_PRESS(0x34))
                    			printf("4\n");
                    		else if (KEY_PRESS(0x35))
                    			printf("5\n");
                    		else if (KEY_PRESS(0x36))
                    			printf("6\n");
                    		else if (KEY_PRESS(0x37))
                    			printf("7\n");
                    	}
                    	return 0;
                    }
                    

                    2.游戏设计与分析

                    2.1地图设计

                    由于我打印墙体和蛇想使用 □,但是它是宽字符,该如何打印呢?

                    【贪吃蛇:C语言实现】,在这里插入图片描述,第11张

                    C语言规定:宽字符的类型wchar_t 和宽字符的输⼊和输出函数。

                    头文件为:

                    【贪吃蛇:C语言实现】,在这里插入图片描述,第12张

                    宽字符与普通字符对比:

                    【贪吃蛇:C语言实现】,在这里插入图片描述,第13张

                    由于墙体使用的是宽字符,所以在打印时需要注意!

                    【贪吃蛇:C语言实现】,在这里插入图片描述,第14张

                    【贪吃蛇:C语言实现】,在这里插入图片描述,第15张

                    以下函数实现打印墙体的功能:

                    #define WALL L'□'
                    void DrawMap()
                    {
                    	SetPos(0, 0);
                    	//上
                    	for (int i = 0; i <= 56; i += 2)
                    		wprintf(L"%lc", WALL);
                    	//左、右
                    	for (int i = 1; i <= 25; i++)
                    	{
                    		SetPos(0, i);
                    		wprintf(L"%lc", WALL);
                    		SetPos(56, i);
                    		wprintf(L"%lc", WALL);
                    	}
                    	//下
                    	SetPos(0, 26);
                    	for (int i = 0; i <= 56; i += 2)
                    		wprintf(L"%lc", WALL);  
                    }
                    

                    再打印地图之前,我们需要设置窗口的大小,并且给玩家一些引导(Win32API的使用已在上面讲过)

                    //窗口相关设置
                    void ScreenPrepare()
                    {
                    	//设置窗体大小
                    	system("mode con cols=100 lines=40");
                    	system("title 贪吃蛇小游戏");
                    	//隐藏光标
                    	HideCurSor();
                    	//打印欢迎语
                    	Welcome();
                    	//打印地图
                    	DrawMap();
                    	//显示提示信息
                    	HelpInfo();
                    }
                    

                    具体函数的实现我们会在后面给出,先看一下效果吧~

                    【贪吃蛇:C语言实现】,在这里插入图片描述,第16张

                    2.2数据结构的设计

                    2.2.1蛇身的设计

                    在游戏运行的过程中,蛇每次吃⼀个⻝物,蛇的⾝体就会变长⼀节,如果我们使用链表存储蛇的信息,那么蛇的每⼀节其实就是链表的每个节点。每个节点只要记录好蛇身节点在地图上的坐标就行,所以蛇节点结构如下:

                    //蛇节点的定义
                    typedef struct SnakeNode
                    {
                    	int x;
                    	int y;
                    	struct SnakeNode* next;//指向下一个节点
                    }SnakeNode,*pSnakeNode;//同时定义一个指向该节点的指针
                    

                    2.2.2蛇的方向

                    蛇的方向可以一一列举,使用枚举

                    //蛇的方向
                    //上、下、做、左、右
                    enum Direction
                    {
                    	UP = 1,
                    	DOWN,
                    	LEFT,
                    	RIGHT
                    };
                    

                    2.2.3游戏的状态

                    游戏状态,可以⼀⼀列举,使用枚举

                    //游戏的状态
                    enum GameState
                    {
                    	OK,//正常
                    	KILL_BY_WALL,//撞墙
                    	KILL_BY_SELF,//撞到自己
                    	END//结束
                    };
                    

                    2.2.4管理整条蛇

                    我们再封装⼀个Snake的结构体来维护整条贪吃蛇。

                    //整条蛇
                    typedef struct Snake
                    {
                    	pSnakeNode pSnake;  //指向整条蛇的指针
                    	pSnakeNode PFood;   //指向食物的指针
                    	enum Direction Dire;  //蛇的方向
                    	enum GameState State; //蛇的状态
                    	int SleepTime; //蛇的休眠时间;休眠越短,蛇移动的越快
                    	int Score;//游戏得分
                    	int FoodScore;//一个食物的分数
                    }Snake,* pSnake;
                    

                    2.3.游戏流程

                    【贪吃蛇:C语言实现】,在这里插入图片描述,第17张

                    3. 功能的具体实现

                    游戏开始前

                    3.1 初始化蛇

                    游戏开始,蛇的长度默认是3。

                    • 创建节点,头插法依次连接
                    • 节点的纵坐标相同,横坐标依次增加2,产生串的效果
                    • 蛇的方向默认为右
                    • 打印蛇,并且为蛇头设置颜色

                      此处颜色设置我们使用Win32API提供的函数:

                      BOOL WINAPI SetConsoleTextAttribute(
                        _In_ HANDLE hConsoleOutput,//当前设备句柄
                        _In_ WORD   wAttributes//颜色
                      );
                      

                      【贪吃蛇:C语言实现】,在这里插入图片描述,第18张

                      //设置颜色
                      void SetColor(int HeadColor)
                      {
                      	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), HeadColor);
                      }
                      //打印蛇
                      void PrintSnake(pSnake ps)
                      {
                      	pSnakeNode cur = ps->pSnake;
                      	int flag = 1;
                      	while (cur)
                      	{
                      		if (flag == 1)
                      		{
                      			//为蛇头设置颜色
                      			SetColor(12);
                      		}
                      		SetPos(cur->x, cur->y);//设置每个节点的位置
                      		wprintf(L"%lc", BODY);
                      		cur = cur->next;
                      		flag = 0;
                      		SetColor(10);
                      	}
                      }
                      //初始化蛇
                      void InitSnake(pSnake ps)
                      {
                      	int i = 0;
                      	pSnakeNode cur = NULL;
                      	//蛇的长度开始为3
                      	for (i = 0; i < 3; i++)
                      	{
                      		cur = (pSnakeNode)malloc(sizeof(SnakeNode));
                      		if (cur == NULL)
                      		{
                      			perror("InitSnake():malloc");
                      			return;
                      		}
                      		cur->next = NULL;
                      		cur->x = POS_X + i * 2;  //节点横坐标依次增加2
                      		cur->y = POS_Y;
                      		//头插法将节点相连
                      		if (ps->pSnake == NULL)
                      		{
                      			ps->pSnake = cur;
                      		}
                      		else
                      		{
                      			cur->next = ps->pSnake;
                      			ps->pSnake = cur;
                      		}
                      	}
                      	//打印蛇
                      	PrintSnake(ps);
                      	//初始化蛇的其它信息
                      	ps->Dire = RIGHT;
                      	ps->FoodScore = 10;
                      	ps->SleepTime = 200;
                      	ps->State = OK;
                      	ps->Score = 0;
                      }
                      

                      3.2 创建食物

                      食物要随机生成,有以下几个注意事项:

                      • 食物应该在墙体内
                      • 食物不可与蛇身重叠
                      • 由于我们蛇的打印使用的是宽字符,所以食物的坐标应该在2的倍数处
                        //创建食物
                        void CreatFood(pSnake ps)
                        {
                        	int x = 0;
                        	int y = 0;
                        	//食物坐标应该在墙体内
                        again:
                        	do
                        	{
                        		x = rand() % 53 + 2;
                        		y = rand() % 25 + 1;
                        	} while (x % 2 != 0);
                        	//食物坐标不与蛇重叠
                        	pSnakeNode cur = ps->pSnake;
                        	while (cur)
                        	{
                        		if (x == cur->x && y == cur->y)
                        		{
                        			goto again;
                        		}
                        		cur = cur->next;
                        	}
                        	//根据坐标创建食物
                        	pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
                        	if (pFood == NULL)
                        	{
                        		perror("CreatFood():malloc()");
                        		return;
                        	}
                        	//打印食物
                        	pFood->x = x;
                        	pFood->y = y;
                        	SetColor(14);
                        	SetPos(pFood->x, pFood->y);
                        	wprintf(L"%lc", FOOD);
                        	SetColor(10);
                        	ps->pFood = pFood;
                        }
                        

                        【贪吃蛇:C语言实现】,在这里插入图片描述,第19张

                        游戏运行

                        3.3 检测按键

                        玩家按上下左右键控制蛇的移动,在检测时需要注意:

                        • 玩家按的键若与蛇的方向相反,则不做响应;玩家按键符合,则修改蛇的方向
                        • 玩家按F1或F2修改蛇的移动速度;蛇的速度应大于0,食物的分数最低为1分
                        • 按键的检测使用Win32API提供的功能:GetAsyncKeyState
                          //玩游戏
                          void GameRun(pSnake ps)
                          {
                          	do
                          	{
                          		//显示分数
                          		SetColor(12);
                          		SetPos(65, 12);
                          		printf("目前得分:%-5d",ps->Score);
                          		SetColor(10);
                          		SetPos(65, 13);
                          		printf("每个食物:%2d分",ps->FoodScore);
                          		//检测按键
                          		if (KEY_PRESS(VK_UP) && ps->Dire != DOWN)
                          		{
                          			//按上键,且蛇的方向不能向下
                          			ps->Dire = UP;
                          		}
                          		else if (KEY_PRESS(VK_DOWN) && ps->Dire != UP)
                          		{
                          			//按下键,且蛇的方向不能向上
                          			ps->Dire = DOWN;
                          		}
                          		else if (KEY_PRESS(VK_LEFT) && ps->Dire != RIGHT)
                          		{
                          			//按左键,且蛇的方向不能向右
                          			ps->Dire = LEFT;
                          		}
                          		else if (KEY_PRESS(VK_RIGHT) && ps->Dire != LEFT)
                          		{
                          			//按右键,且蛇的方向不能向左
                          			ps->Dire = RIGHT;
                          		}
                          		else if (KEY_PRESS(VK_ESCAPE))
                          		{
                          			//按ESC,退出游戏
                          			ps->State = END;
                          		}
                          		else if (KEY_PRESS(VK_SPACE))
                          		{
                          			//按空格,暂停
                          			pause();
                          		}
                          		else if (KEY_PRESS(VK_F1))
                          		{
                          			//按F1加速,即睡眠时间变短
                          			
                          			//休眠时间不能是负数,最快就是休眠30ms
                          			if (ps->SleepTime >= 50)
                          			{
                          				ps->SleepTime -= 20;
                          				//速度变快,食物的分数变高
                          				ps->FoodScore += 2;
                          			}
                          		}
                          		else if (KEY_PRESS(VK_F2))
                          		{
                          			//F2减速,睡眠时间变长
                          			//食物的分数不能减到负数,最多减为1分
                          			if (ps->FoodScore >= 3)
                          			{
                          				ps->SleepTime += 20;
                          				ps->FoodScore -= 2;
                          			}
                          		}
                          		//按照蛇的睡眠时间,真正实现休眠
                          		Sleep(ps->SleepTime);
                          		//休眠后,蛇要移动
                          		SnakeMove(ps);
                          	} while (ps->State == OK);//只有蛇的状态为OK,才能继续检测按键
                          }
                          

                          3.4 蛇移动

                          蛇的移动就是根据蛇的方向,产生节点,判断节点是不是食物。

                          • 所产生的节点的位置根据蛇的方向而定
                          • 节点是食物,吃掉食物,蛇身变长
                          • 节点不是食物,也吃掉食物,但长度不变
                            //移动蛇
                            void SnakeMove(pSnake ps)
                            {
                            	//先产生节点
                            	pSnakeNode pNext = (pSnakeNode)malloc(sizeof(SnakeNode));
                            	if (pNext == NULL)
                            	{
                            		perror("SnakeMove():malloc()");
                            		return;
                            	}
                            	//根据蛇的方向,设定节点的位置
                            	switch (ps->Dire)
                            	{
                            	case UP:
                            		//蛇的方向向上,节点应该在当前蛇头的上方;横坐标不变,纵坐标减1
                            		pNext->x = ps->pSnake->x;
                            		pNext->y = ps->pSnake->y - 1;
                            		break;
                            	case DOWN:
                            		//蛇的方向向下,节点应该在当前蛇头的下方;横坐标不变,纵坐标加1
                            		pNext->x = ps->pSnake->x;
                            		pNext->y = ps->pSnake->y + 1;
                            		break;
                            	case LEFT:
                            		//蛇的方向向左,节点应该在当前蛇头的左边;纵坐标不变,横坐标减2
                            		pNext->x = ps->pSnake->x - 2;
                            		pNext->y = ps->pSnake->y;
                            		break;
                            	case RIGHT:
                            		//蛇的方向向右,节点应该在当前蛇头的右方;纵坐标不变,横坐标加2
                            		pNext->x = ps->pSnake->x + 2;
                            		pNext->y = ps->pSnake->y;
                            		break;
                            	}
                            	//判断下一个节点是否是食物
                            	if (JudgeNext(ps,pNext))
                            	{
                            		//是食物,吃掉,长度增加
                            		EatFood(ps, pNext);
                            	}
                            	else
                            	{
                            		//不是食物,吃掉,长度不增加
                            		NoFood(ps, pNext);
                            	}
                            	//未完
                            	//撞墙
                            	
                            	//撞自己
                            }
                            

                            3.5 判断节点是否是食物

                            将所产生的节点与食物的节点对比即可

                            //判断节点是否是食物
                            int JudgeNext(pSnake ps, pSnakeNode pNext)
                            {
                            	return ((ps->pFood->x == pNext->x) && (ps->pFood->y == pNext->y));
                            }
                            

                            3.6 吃食物

                            吃掉食物很简单,将所产生的节点与蛇想连;连接后得分增加,并释放所产生的节点,再次产生食物。

                            //是食物,吃掉,长度增加
                            void EatFood(pSnake ps, pSnakeNode pNext)
                            {
                            	//吃掉食物,头插法将节点插入
                            	pNext->next = ps;
                            	ps = pNext;
                            	//打印蛇
                            	PrintSnake(ps_->snake);
                            	//加分
                            	PrintSnake(ps->pSnake);
                            	//释放食物节点
                            	free(ps->pFood);
                            	//再次创建食物
                            	CreatFood(ps);
                            }
                            

                            3.7 不吃食物

                            不吃食物需要将产生的节点与蛇相连,然后删除蛇尾(将蛇尾打印尾空格,并释放蛇尾节点)

                            //不吃食物
                            void NoFood(pSnake ps, pSnakeNode pNext)
                            {
                            	//头插法连接
                            	pNext->next = ps->pSnake;
                            	ps->pSnake = pNext;
                            	//删除蛇尾
                            	pSnakeNode cur = ps->pSnake;
                            	//找到蛇尾的前一个节点
                            	while (cur->next->next)
                            	{
                            		cur = cur->next;
                            	}
                            	pSnakeNode del = cur->next;
                            	//将蛇尾打印尾空格
                            	SetPos(del->x, del->y);
                            	printf("  ");
                            	//释放蛇尾节点
                            	free(del);
                            	cur->next = NULL;
                            	//打印蛇
                            	PrintSnake(ps);
                            }
                            

                            3.8 撞墙

                            若蛇头坐标与墙体坐标重合,则说明撞墙了。

                            //撞墙
                            void KillByWall(pSnake ps)
                            {
                            	if (ps->pSnake->x == 0 ||
                            		ps->pSnake->x == 56 ||
                            		ps->pSnake->y == 0 ||
                            		ps->pSnake->y == 26)
                            	{
                            		ps->State = KillByWall;
                            	}
                            }
                            

                            3.9 撞到自己

                            若蛇头坐标与蛇身节点的坐标重合,则说明撞到自己了。

                            //撞到自己
                            void KillBySelf(pSnake ps)
                            {
                            	pSnakeNode cur = ps->pSnake->next;
                            	while (cur)
                            	{
                            		if (cur->x == ps->pSnake->x && cur->y == ps->pSnake->y)
                            		{
                            			ps->State = KILL_BY_SELF;
                            		}
                            		cur = cur->next;
                            	}
                            }
                            

                            3.10 游戏结束

                            给出游戏结束的原因,释放蛇身节点。

                            //游戏结束
                            void GameEnd(pSnake ps)
                            {
                            	SetPos(20, 13);
                            	SetColor(12);
                            	switch (ps->State)
                            	{
                            	case END:
                            		printf("您结束了游戏");
                            		break;
                            	case KILL_BY_SELF:
                            		printf("很遗憾!您撞到了自己");
                            		break;
                            	case KILL_BY_WALL:
                            		printf("很遗憾!您撞墙了");
                            		break;
                            	}
                            	pSnakeNode cur = ps->pSnake;
                            	while (cur)
                            	{
                            		pSnakeNode del = cur;
                            		cur = cur->next;
                            		free(del);
                            	}
                            	ps = NULL;
                            	SetColor(10);
                            }
                            

                            3.11 main函数

                            让玩家选择

                            int main()
                            {
                            	srand((unsigned int)time(NULL));
                            	setlocale(LC_ALL, "");//适应本地中文环境
                            	int ch = 0;
                            	do
                            	{
                            		Snake snake = { 0 };//创建贪吃蛇
                            		//游戏开始前的初始化
                            		GameStart(&snake);
                            		玩游戏
                            		GameRun(&snake);
                            		游戏结束,善后工作
                            		GameEnd(&snake);
                            		SetPos(20, 15);
                            		printf("还要再来一局吗?(Y/N)");
                            		ch = getchar();
                            		getchar();//吸收换行
                            	} while (ch == 'Y' || ch == 'y');
                            	SetPos(0, 30);
                            	return 0;
                            }
                            

                            4.参考代码

                            4.1 main

                            #define _CRT_SECURE_NO_WARNINGS 1
                            #include"Snake.h"
                            int main()
                            {
                            	srand((unsigned int)time(NULL));
                            	setlocale(LC_ALL, "");//适应本地中文环境
                            	int ch = 0;
                            	do
                            	{
                            		Snake snake = { 0 };//创建贪吃蛇
                            		//游戏开始前的初始化
                            		GameStart(&snake);
                            		玩游戏
                            		GameRun(&snake);
                            		游戏结束,善后工作
                            		GameEnd(&snake);
                            		SetPos(20, 15);
                            		printf("还要再来一局吗?(Y/N)");
                            		ch = getchar();
                            		getchar();//吸收换行
                            	} while (ch == 'Y' || ch == 'y');
                            	SetPos(0, 30);
                            	return 0;
                            }
                            

                            4.2 Snake.h

                            #define _CRT_SECURE_NO_WARNINGS 1
                            #include
                            #include
                            #include
                            #include
                            #include
                            #include
                            #define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)
                            #define WALL L'□'//墙体
                            #define BODY L'●'//蛇身
                            #define FOOD L'★'//食物
                            //蛇初始位置
                            #define POS_X 24
                            #define POS_Y 12
                            //蛇的方向
                            //上、下、左、右
                            enum Direction
                            {
                            	
                            	UP = 1,
                            	DOWN,
                            	LEFT,
                            	RIGHT
                            };
                            //游戏的状态
                            enum GameState
                            {
                            	OK,//正常
                            	KILL_BY_WALL,//撞墙
                            	KILL_BY_SELF,//撞到自己
                            	END//结束
                            };
                            //蛇节点的定义
                            typedef struct SnakeNode
                            {
                            	int x;
                            	int y;
                            	struct SnakeNode* next;//指向下一个节点
                            }SnakeNode,*pSnakeNode;//同时定义一个指向该节点的指针
                            //整条蛇
                            typedef struct Snake
                            {
                            	pSnakeNode pSnake;  //指向整条蛇的指针
                            	pSnakeNode pFood;   //指向食物的指针
                            	enum Direction Dire;  //蛇的方向
                            	enum GameState State; //蛇的状态
                            	int SleepTime; //蛇的休眠时间;休眠越短,蛇移动的越快
                            	int Score;//游戏得分
                            	int FoodScore;//一个食物的分数
                            }Snake,* pSnake;
                            //设置光标位置
                            void SetPos(int x, int y);
                            //设置颜色
                            void SetColor(int HeadColor);
                            //游戏准备
                            void GameStart(pSnake psnake);
                            //初始化蛇
                            void InitSnake(pSnake psnake);
                            //创建食物
                            void CreatFood(pSnake ps);
                            //打印蛇
                            void PrintSnake(pSnake ps);
                            //玩游戏
                            void GameRun(pSnake ps);
                            //移动蛇
                            void SnakeMove(pSnake ps);
                            //判断节点是否是食物
                            int JudgeNext(pSnake ps, pSnakeNode pNext);
                            //是食物,吃掉,长度增加
                            void EatFood(pSnake ps, pSnakeNode pNext);
                            //不是食物
                            void NoFood(pSnake ps, pSnakeNode pNext);
                            //撞墙
                            void KillByWall(pSnake ps);
                            //撞到自己
                            void KillBySelf(pSnake ps);
                            //游戏结束
                            void GameEnd(pSnake ps);
                            

                            4.3 Snake.c

                            #define _CRT_SECURE_NO_WARNINGS 1
                            #include"Snake.h"
                            //隐藏光标
                            void HideCurSor()
                            {
                            	CONSOLE_CURSOR_INFO cursor = { 0 };
                            	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
                            	GetConsoleCursorInfo(handle, &cursor);
                            	cursor.bVisible = false;
                            	SetConsoleCursorInfo(handle, &cursor);
                            }
                            //设置光标位置
                            void SetPos(int x, int y)
                            {
                            	COORD pos = { x,y }; //要设置的位置
                            	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//哪个设备
                            	SetConsoleCursorPosition(handle, pos);//设置
                            }
                            void Welcome()
                            {
                            	//显示欢迎语一
                            	SetPos(38, 15);
                            	printf("欢迎来到贪吃蛇小游戏!");
                            	SetPos(40, 24);
                            	system("pause");
                            	system("cls");//清屏
                            	//提示语
                            	SetPos(25, 15);
                            	printf("按 ↑、↓、←、→ 控制蛇蛇的移动,F1为加速,F2为减速,");
                            	SetPos(25, 16);
                            	printf("加速将能得到更高的分数");
                            	SetPos(40, 24);
                            	system("pause");
                            }
                            //打印地图
                            void DrawMap()
                            {
                            	system("cls");
                            	SetColor(6);
                            	SetPos(0, 0);
                            	//上
                            	for (int i = 0; i <= 56; i += 2)
                            		wprintf(L"%lc", WALL);
                            	//左、右
                            	for (int i = 1; i <= 25; i++)
                            	{
                            		SetPos(0, i);
                            		wprintf(L"%lc", WALL);
                            		SetPos(56, i);
                            		wprintf(L"%lc", WALL);
                            	}
                            	//下
                            	SetPos(0, 26);
                            	for (int i = 0; i <= 56; i += 2)
                            		wprintf(L"%lc", WALL);  
                            	SetColor(10);
                            }
                            //显示提示信息
                            void HelpInfo()
                            {
                            	SetPos(65, 18);
                            	printf("不能穿墙,不能碰到自己,");
                            	SetPos(65, 19);
                            	printf("按 ↑、↓、←、→ 控制蛇蛇的移动,");
                            	SetPos(65, 21);
                            	printf("F1为加速,F2为减速,");
                            	SetPos(65, 22);
                            	printf("ESC:退出游戏 SPACE:暂停");
                            }
                            //窗口相关设置
                            void ScreenPrepare()
                            {
                            	//设置窗体大小
                            	system("mode con cols=100 lines=40");
                            	system("title 贪吃蛇小游戏");
                            	//隐藏光标
                            	HideCurSor();
                            	//打印欢迎语
                            	Welcome();
                            	//打印地图
                            	DrawMap();
                            	//显示提示信息
                            	HelpInfo();
                            }
                            //设置颜色
                            void SetColor(int HeadColor)
                            {
                            	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), HeadColor);
                            }
                            //打印蛇
                            void PrintSnake(pSnake ps)
                            {
                            	pSnakeNode cur = ps->pSnake;
                            	int flag = 1;
                            	while (cur)
                            	{
                            		if (flag == 1)
                            		{
                            			//为蛇头设置颜色
                            			SetColor(12);
                            		}
                            		SetPos(cur->x, cur->y);//设置每个节点的位置
                            		wprintf(L"%lc", BODY);
                            		cur = cur->next;
                            		flag = 0;
                            		SetColor(10);
                            	}
                            }
                            //初始化蛇
                            void InitSnake(pSnake ps)
                            {
                            	int i = 0;
                            	pSnakeNode cur = NULL;
                            	//蛇的长度开始为3
                            	for (i = 0; i < 5; i++)
                            	{
                            		cur = (pSnakeNode)malloc(sizeof(SnakeNode));
                            		if (cur == NULL)
                            		{
                            			perror("InitSnake():malloc");
                            			return;
                            		}
                            		cur->next = NULL;
                            		cur->x = POS_X + i * 2;  //节点横坐标依次增加2
                            		cur->y = POS_Y;
                            		//头插法将节点相连
                            		if (ps->pSnake == NULL)
                            		{
                            			ps->pSnake = cur;
                            		}
                            		else
                            		{
                            			cur->next = ps->pSnake;
                            			ps->pSnake = cur;
                            		}
                            	}
                            	//打印蛇
                            	PrintSnake(ps);
                            	//初始化蛇的其它信息
                            	ps->Dire = RIGHT;
                            	ps->FoodScore = 10;
                            	ps->SleepTime = 200;
                            	ps->State = OK;
                            	ps->Score = 0;
                            }
                            //创建食物
                            void CreatFood(pSnake ps)
                            {
                            	int x = 0;
                            	int y = 0;
                            	//食物坐标应该在墙体内
                            again:
                            	do
                            	{
                            		x = rand() % 53 + 2;
                            		y = rand() % 25 + 1;
                            	} while (x % 2 != 0);
                            	//食物坐标不与蛇重叠
                            	pSnakeNode cur = ps->pSnake;
                            	while (cur)
                            	{
                            		if (x == cur->x && y == cur->y)
                            		{
                            			goto again;
                            		}
                            		cur = cur->next;
                            	}
                            	//根据坐标创建食物
                            	pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
                            	if (pFood == NULL)
                            	{
                            		perror("CreatFood():malloc()");
                            		return;
                            	}
                            	//打印食物
                            	pFood->x = x;
                            	pFood->y = y;
                            	SetColor(14);
                            	SetPos(pFood->x, pFood->y);
                            	wprintf(L"%lc", FOOD);
                            	SetColor(10);
                            	ps->pFood = pFood;
                            }
                            //游戏准备
                            void GameStart(pSnake psnake)
                            {
                            	//设置好窗口、地图
                            	ScreenPrepare();
                            	//初始化蛇
                            	InitSnake(psnake);
                            	//创建食物
                            	CreatFood(psnake);
                            }
                            //暂停
                            void pause()
                            {
                            	while (1)
                            	{
                            		//一直休眠,直到再次按空格键
                            		Sleep(200);
                            		if (KEY_PRESS(VK_SPACE))
                            		{
                            			break;
                            		}
                            	}
                            }
                            //玩游戏
                            void GameRun(pSnake ps)
                            {
                            	do
                            	{
                            		//显示分数
                            		SetColor(12);
                            		SetPos(65, 12);
                            		printf("目前得分:%-5d",ps->Score);
                            		SetColor(10);
                            		SetPos(65, 13);
                            		printf("每个食物:%2d分",ps->FoodScore);
                            		//检测按键
                            		if (KEY_PRESS(VK_UP) && ps->Dire != DOWN)
                            		{
                            			//按上键,且蛇的方向不能向下
                            			ps->Dire = UP;
                            		}
                            		else if (KEY_PRESS(VK_DOWN) && ps->Dire != UP)
                            		{
                            			//按下键,且蛇的方向不能向上
                            			ps->Dire = DOWN;
                            		}
                            		else if (KEY_PRESS(VK_LEFT) && ps->Dire != RIGHT)
                            		{
                            			//按左键,且蛇的方向不能向右
                            			ps->Dire = LEFT;
                            		}
                            		else if (KEY_PRESS(VK_RIGHT) && ps->Dire != LEFT)
                            		{
                            			//按右键,且蛇的方向不能向左
                            			ps->Dire = RIGHT;
                            		}
                            		else if (KEY_PRESS(VK_ESCAPE))
                            		{
                            			//按ESC,退出游戏
                            			ps->State = END;
                            		}
                            		else if (KEY_PRESS(VK_SPACE))
                            		{
                            			//按空格,暂停
                            			pause();
                            		}
                            		else if (KEY_PRESS(VK_F1))
                            		{
                            			//按F1加速,即睡眠时间变短
                            			
                            			//休眠时间不能是负数,最快就是休眠30ms
                            			if (ps->SleepTime >= 50)
                            			{
                            				ps->SleepTime -= 20;
                            				//速度变快,食物的分数变高
                            				ps->FoodScore += 2;
                            			}
                            		}
                            		else if (KEY_PRESS(VK_F2))
                            		{
                            			//F2减速,睡眠时间变长
                            			//食物的分数不能减到负数,最多减为1分
                            			if (ps->FoodScore >= 3)
                            			{
                            				ps->SleepTime += 20;
                            				ps->FoodScore -= 2;
                            			}
                            		}
                            		//按照蛇的睡眠时间,真正实现休眠
                            		Sleep(ps->SleepTime);
                            		//休眠后,蛇要移动
                            		SnakeMove(ps);
                            	} while (ps->State == OK);//只有蛇的状态为OK,才能继续检测按键
                            }
                            //移动蛇
                            void SnakeMove(pSnake ps)
                            {
                            	//先产生节点
                            	pSnakeNode pNext = (pSnakeNode)malloc(sizeof(SnakeNode));
                            	if (pNext == NULL)
                            	{
                            		perror("SnakeMove():malloc()");
                            		return;
                            	}
                            	//根据蛇的方向,设定节点的位置
                            	switch (ps->Dire)
                            	{
                            	case UP:
                            		//蛇的方向向上,节点应该在当前蛇头的上方;横坐标不变,纵坐标减1
                            		pNext->x = ps->pSnake->x;
                            		pNext->y = ps->pSnake->y - 1;
                            		break;
                            	case DOWN:
                            		//蛇的方向向下,节点应该在当前蛇头的下方;横坐标不变,纵坐标加1
                            		pNext->x = ps->pSnake->x;
                            		pNext->y = ps->pSnake->y + 1;
                            		break;
                            	case LEFT:
                            		//蛇的方向向左,节点应该在当前蛇头的左边;纵坐标不变,横坐标减2
                            		pNext->x = ps->pSnake->x - 2;
                            		pNext->y = ps->pSnake->y;
                            		break;
                            	case RIGHT:
                            		//蛇的方向向右,节点应该在当前蛇头的右方;纵坐标不变,横坐标加2
                            		pNext->x = ps->pSnake->x + 2;
                            		pNext->y = ps->pSnake->y;
                            		break;
                            	}
                            	//判断下一个节点是否是食物
                            	if (JudgeNext(ps, pNext))
                            	{
                            		//是食物,吃掉,长度增加
                            		EatFood(ps, pNext);
                            	}
                            	else
                            	{
                            		//不是食物,吃掉,长度不增加
                            		NoFood(ps, pNext);
                            	}
                            	//撞墙
                            	KillByWall(ps);
                            	//撞自己
                            	KillBySelf(ps);
                            }
                            //判断节点是否是食物
                            int JudgeNext(pSnake ps, pSnakeNode pNext)
                            {
                            	return ((ps->pFood->x == pNext->x) && (ps->pFood->y == pNext->y));
                            }
                            //是食物,吃掉,长度增加
                            void EatFood(pSnake ps, pSnakeNode pNext)
                            {
                            	//吃掉食物,头插法将节点插入
                            	pNext->next = ps->pSnake;
                            	ps->pSnake = pNext;
                            	//打印蛇
                            	PrintSnake(ps);
                            	//加分
                            	ps->Score +=  ps->FoodScore;
                            	//释放食物节点
                            	free(ps->pFood);
                            	//再次创建食物
                            	CreatFood(ps);
                            }
                            //不是食物
                            void NoFood(pSnake ps, pSnakeNode pNext)
                            {
                            	//头插法连接
                            	pNext->next = ps->pSnake;
                            	ps->pSnake = pNext;
                            	//删除蛇尾
                            	pSnakeNode cur = ps->pSnake;
                            	//找到蛇尾的前一个节点
                            	while (cur->next->next)
                            	{
                            		cur = cur->next;
                            	}
                            	pSnakeNode del = cur->next;
                            	//将蛇尾打印为空格
                            	SetPos(del->x, del->y);
                            	printf("  ");
                            	//释放蛇尾节点
                            	free(del);
                            	cur->next = NULL;
                            	//打印蛇
                            	PrintSnake(ps);
                            }
                            //撞墙
                            void KillByWall(pSnake ps)
                            {
                            	if (ps->pSnake->x == 0 ||
                            		ps->pSnake->x == 56 ||
                            		ps->pSnake->y == 0 ||
                            		ps->pSnake->y == 26)
                            	{
                            		ps->State = KILL_BY_WALL;
                            	}
                            }
                            //撞到自己
                            void KillBySelf(pSnake ps)
                            {
                            	pSnakeNode cur = ps->pSnake->next;
                            	while (cur)
                            	{
                            		if (cur->x == ps->pSnake->x && cur->y == ps->pSnake->y)
                            		{
                            			ps->State = KILL_BY_SELF;
                            		}
                            		cur = cur->next;
                            	}
                            }
                            //游戏结束
                            void GameEnd(pSnake ps)
                            {
                            	SetPos(20, 13);
                            	SetColor(12);
                            	switch (ps->State)
                            	{
                            	case END:
                            		printf("您结束了游戏");
                            		break;
                            	case KILL_BY_SELF:
                            		printf("很遗憾!您撞到了自己");
                            		break;
                            	case KILL_BY_WALL:
                            		printf("很遗憾!您撞墙了");
                            		break;
                            	}
                            	pSnakeNode cur = ps->pSnake;
                            	while (cur)
                            	{
                            		pSnakeNode del = cur;
                            		cur = cur->next;
                            		free(del);
                            	}
                            	ps = NULL;
                            	SetColor(10);
                            }
                            

网友评论

搜索
最新文章
热门文章
热门标签
 
 梦见多年不见的老同学  梦见大开杀戒  梦见掉牙家里有丧事