C语言贪吃蛇 新手入门(超详细)

C语言贪吃蛇 新手入门(超详细)贪吃蛇小程序,主要采用C语言描述,采用数组进行数据存储的图形化操作界面,会涉及一点点C++知识(特别少的一点点),但是有C语言基础就够用了,编译器:VS2015,会涉及部分库函数的安装及使用,我会在文章中给出详细的安装方法和解决方案。文章目录文章目录前言一、话不多述,先看游戏效果二、实现步骤1.新建工程文件及引入库函数2.框架设计和代码实现实现设想:1.游戏界面2.绘制游戏元素3.获取按键信息4.操作贪吃蛇(重点)5.贪吃长大6.判断游戏结..

贪吃蛇小程序,主要采用C语言描述,采用数组进行数据存储的图形化操作界面,会涉及一点点C++ 知识(特别少的一点点),但是有C语言基础就够用了,编译器:VS2015,会涉及部分库函数的安装及使用,我会在文章中给出详细的安装方法和解决方案。



前言

在学习C语言的过程中,用小游戏来提高自己的代码实现能力是很不错的,贪吃蛇小程序可以在学习完数组的相关知识后有了一定的代码基础就可以尝试了,由于本游戏采用了图形化界面,可以作为平时的大作业和操作练手,下面我将带着用C语言写一个贪吃蛇小程序,包含图形界面和背景音乐,主要使用数组来实现数据存储,我使用的是VS2015,关于代码兼容性的问题,我会提及并解释,关于各种操作的原理和函数的调用,我会给出详细的解释和参考来源,对小白及其友好,下面,让我们开始吧!


一、话不多述,先看游戏效果

请原谅我那手残的操作 ε=(´ο`*))),(视频是有背景音乐的,但是由于视频不好上传,就只上传了GIF)

贪吃蛇实现效果
贪吃蛇效果

不过可以看到,实现效果还是不错的嘛,对于基本的按键操作都有很好的应对,并且在边界是可以自由穿过和穿出的,一定程度上降低了游戏难度,并且这里我设置的是半秒钟的延迟,对我来说已经比较快了,希望你们可以挑战更高的难度 ε≡٩(๑>₃<)۶ 

二、实现步骤


1.新建工程文件及引入库函数

新建文件:基本的新建文件及关闭安全检查在这里我就不提了,如果需要可以参考往期博文:如何关闭VS安全检查—三种方法

这里唯一要注意的是在添加文件时要添加 C++ 文件,而不是C文件,因为有使用easyx,必须是C++ 文件,具体细节可以参考EasyX说明文档,这是链接:easyx说明文档

引入库函数:因为我要进行的是图形操作,所以就会设计图形化的库函数引用,可以在VS编译器中是没有预装的,就可以自己选择安装了,这里我也有写过,可以参考往期博文:easyx等图形化库函数的使用  

好了,截止目前,我们要做的前期工作就基本完成了,下面要做的就是构建实现框架,编写代码了!(σ゚∀゚)σ..:*☆


2.框架设计和代码实现 

实现设想:

首先要有一个图形化的游戏界面,而不一个简单的操作台,也就是我们为什么要在上面要安装easyx了,我所设想的操作界面的大小是640*480的一个矩形,蛇体本身用方形表示,以10*10为基础构成,初始状态为三节的长度,没吃到一个 食物 ,身体长度就增加一节,并且蛇的操作是由方向键控制的,就需要在图形化界面里获取按键信息,并且要可以实时获得蛇的位置信息,我所设计的是可以穿过边界,并在另一侧出现,这样可以减少游戏难度,并且蛇的身体是要不断增加的,在这里我采用的是用数组来实现蛇的身体的增加,每吃到一个食物,将让该数组的长度加一,下面就让我们开始一步一步的实现这些设想吧。

1.游戏界面

首先就是要先实现这有640*480的游戏界面了,我选择定义了两个常量来存储这个数值,并且一起定义了蛇的最大长度在100

#define WIN_WIDTH 640
#define WIN_HEIGHT 480
#define MAX_SNAKE 100  //蛇的最大节数

然后就是初始化一个游戏界面了,initgraph 函数的作用就是产生一个图形化的窗口,参数分别是宽度、高度、属性。

具体可以参考说明文档:initgraph 说明文档

initgraph(WIN_WIDTH,WIN_HEIGHT,SHOWCONSOLE);//初始化一个图形窗口
	//具体参数见:https://docs.easyx.cn/zh-cn/initgraph
	printf("\tWelcome! \n");
	printf("\tPlay Game Right Now!\n");

然后就是要设置蛇 的参数,并初始化各个元素了,在这里我使用的是数组来存储蛇的信息,其中就包括了蛇的长度,并且需要实时记录蛇头的方向(方便操作)和位置信息,还需要的就是要统计得分使用的分数了,下面是蛇的结构体:关于其详细的解释,我都写在注释里了关于 POINT 结构体,这个是属于库的结构体,可以直接使用。

struct Snake_tlg  //蛇的结构体
{
	int num;  // 身体的长度
	int dir;  //蛇的方向
	int score;  //分数
	int size;  //蛇的尺寸,身体和高度
	POINT coor[MAX_SNAKE];  //蛇的位置坐标。一个结构体数组
	//POINT 也时一个结构体,,可以通过选中转到定义来查看其具体结构,是在 windef.h中的
	/*
	typedef struct tagPOINT
	{
    LONG  x;
    LONG  y;
	} POINT, *PPOINT, NEAR *NPPOINT, FAR *LPPOINT;
	*/

}snake;

除了蛇的结构体,还要有食物的结构体,我们要记录食物的位置,以及食物是否存在,就是在蛇吃过它之后,它就要消失,并且可以继续产生新的食物,而且这里我还定义了食物的颜色信息,这样可以每次都产生一个不同颜色的食物,具体的实现我会放在 对应的代码中,下面是它的结构体:

struct Food_tlg  //食物的出现 结构体
{
	POINT fd;  //食物的坐标
	int flag;  //是否已经被蛇吃掉
	DWORD color;  //定义食物的颜色
	/*在DirectX程序中有些地方用DWORD(RGBA)格式表示颜色,其范围是[0 - 255]*/
}food;

下面就可以初始化各个游戏元素了,详细的注释我都放在代码里了,这里有一点是关于随机函数的问题,这里使用的是时间来产生随机值的,随机函数的具体使用,我在这个博客的注释中有解释过:三子棋游戏的操作实现_小鸡在啄米的博客-CSDN博客

rand 函数需要头文件<stdlib.h>,要保证每次产生的随机数都不同,就要借用srand,使用当前时间的毫秒数 来作为种子数,产生随机数

void GameInit()
{
	srand((unsigned int)time(NULL));	//time 的头文件是 time.h

	//定义蛇
	snake.num = 3;  //定义蛇初始有三节身体
	snake.dir = RIGHT;  //开始时蛇头向右
	snake.score = 0;  //开始时分数为0
	snake.size = 10;  //初始化蛇的身体大小
	snake.coor[2].x = 0;  //第三节身体(尾巴)的坐标
	snake.coor[2].y = 0;  // 由于初始化时就是在左上角的一个长条,所以y都是0
	snake.coor[1].x = 0+snake.size;  // 蛇的第二节(身体的坐标
	snake.coor[1].y = 0;
	snake.coor[0].x = 0+2*snake.size;  //蛇头,即第一节身体
	snake.coor[0].y = 0;

	//初始化食物
	food.fd.x = rand() % (WIN_WIDTH/10)*10;  //取模确保产生的随机值在范围内
	food.fd.y = rand() % (WIN_HEIGHT/10)*10;
//这里要先对10取商,再乘10是为了产生一个10的倍数,可以让蛇头和食物接触到,而不是每次都是一个随机数,无法判断
	food.flag = 1;  //初始化食物存在
	food.color = RGB(rand() % 256, rand() % 256, rand() % 256);//随机产生食物颜色


}

在初始化基本完成之后,我们就可以进行下面的游戏操作了。

2.绘制游戏元素

关于这一块,主要是对游戏初始的一些设置,这里有个小心机就是在设置舌头的时候要用数组的第一个元素来保存,因为蛇头一定是不变的,永远都是在蛇尾变化,在这里我设置蛇在初始时是三个方块的大小,可以随着吃到食物的个数,随着变化。

设置食物的时候,可以通过绘制函数来绘制其他形状的食物,并且由于我之前在定义食物结构体的时候,有单独定义一个颜色,所以在这里食物的颜色是可以随机变化的,当然了每次的位置也是随机变化的

在设置分数的时候,会设计一个像图形界面输出的文字的问题,当时我按照教程编写的时候,总是无法正常的输出显示,很苦恼,最后在参考了这篇文章才成功解决,博主写的超级详细,很感激(〃’▽’〃),这里贴一下我参考的地址: https://codeabc.cn/yangw/a/error-c2665

所以在本代码中,我参考的是博主的第二个建议,在代码的顶端加上这两句代码,完美解决!!

#undef UNICODE
#undef _UNICODE
void GameDraw()
{
	setbkcolor(RGB(105, 160, 141));  //设置背景颜色,与web开发中的颜色设置一样
	cleardevice();  //清除其他元素

	//画蛇
	setfillcolor(YELLOW);
	int i;
	for (i = 0; i < snake.num; i++)  //每次都画一个矩形,共三次
	{
		setlinecolor(BLACK);
		fillrectangle(snake.coor[i].x,
			snake.coor[i].y,
			snake.coor[i].x + snake.size,
			snake.coor[i].y + snake.size);
		/*在 easyx 中,solidrectangle 这个函数用于画无边框的填充矩形,而fillrectangle用于画有边框的填充矩形
	void solidrectangle(int left,int top,int right,int bottom);
	void solidrectangle(矩形左部 x 坐标,	矩形顶部 y 坐标,	矩形右部 x 坐标,	矩形底部 y 坐标);
		*/

		//printf("%d %d\n", snake.coor[i].x, snake.coor[i].y);  //用于效验坐标,在调试时使用
	}

	//画食物
	if (food.flag == 1) //要确保在食物被吃完之后就消失不见
	{

		setfillcolor(food.color);  //设置食物颜色
		fillellipse(food.fd.x, food.fd.y, food.fd.x + 10, food.fd.y + 10);  //设置食物形状
	}

	//显示分数
	char temp[20] = "";
	sprintf(temp,"分数:%d", snake.score);
	//SetBkMode(TRANSPARENT);
	outtextxy(20, 20, temp);
	//关于这一部分的详细解释参见:https://codeabc.cn/yangw/a/error-c2665

}

好了,到现在,我们就成功实现了游戏的初始化和准备操作,下面就可以实时的对其进行控制了

3.获取按键信息

由于我们是在图形化界面进行的操作,所以我们要获取按键信息也与控制台获取按键信息有些许不同,在这里我主要是参考了其他编程大佬的说法(其实主要是当时查完资料没有保存好网址信息(╥╯^╰╥)):在这里的是我当时的一些理解,将当引用了,如果需要的话可以再自己找一下资料看看  ψ(*`ー´)ψ加油!

可以使用win32API来获取键盘消息,相比使用字符数组来存储并转换为ASCII要快捷很多
    GetAsyncKeyState函数的使用调用,相应按键是处于按下还是弹起状态
    若函数调用成功,返回值指定了自上次调用GetAsyncKeyState以来,指定的按键是否处于按下状态,
    以及该键现在是处于按下还是弹出。如果最高位被置1,表明键被按下,

如果是最低位被置1,则表明自前次调用GetAsyncKeyState一直处于按下状态。

在这里还有一点就是因为要针对方向键来接受信息,同时还可以排除其他操作,在这里使用了枚举类型来定义四个方向,主要是可以很方便的列举四个值,如果有什么不太清楚的可以参考这里的链接:C语言枚举类型(C语言enum用法)详解 

个人感觉写的已经很详细了。下面是枚举类型,在最前面定义的

enum DIR
{
	UP,
	DOWN,
	LEFT,
	RIGHT,
};

下面就是获取按键信息的主要函数了:有关的在注释中也有提及

void KeyControl()
{

	if (GetAsyncKeyState(VK_UP)&&snake.dir!=DOWN)//后面的条件确保了不可以进行反向操作
	{
		snake.dir = UP;
	}
	if (GetAsyncKeyState(VK_DOWN) && snake.dir != UP)
	{
		snake.dir = DOWN;
	}
	if (GetAsyncKeyState(VK_LEFT) && snake.dir != RIGHT)
	{
		snake.dir = LEFT;
	}
	if (GetAsyncKeyState(VK_RIGHT) && snake.dir != LEFT)
	{
		snake.dir = RIGHT;
	}

}

4.操作贪吃蛇(重点)

针对贪吃蛇的操作,主要是在蛇头上,然后蛇的身体只要依次移动到上一个位置就可以了,这样就可以实现贪吃蛇的移动效果(如果是每个的移动都和蛇头一样,那就是俄罗斯方块了),所以我们在这里我们只需要把数组中前一个元素的值 赋值 给后一个元素,就可以了:

int i;
	for (i = snake.num - 1; i > 0; i--)  //因为是对数组进行操作,所以这里要 -1,并且这里是i-- 
	{
		snake.coor[i].x = snake.coor[i - 1].x;
		snake.coor[i].y = snake.coor[i - 1].y;

	}

下面就睡对于蛇头的操作了,从上一个函数中,我们已经获取了蛇头的信息,这里就可以用switch 语句进行匹配就可以了,在收到相应的信息就修改数组中蛇头的位置信息,并且由于我们的设定是可以穿墙的,就要让蛇在到达边界的时候立刻修改蛇头的位置,让其出现在另一侧,实现穿墙效果,下面是该函数的完整实现效果:

void SnakeMove()
{
	/*要操作蛇头的移动,蛇的身体随着蛇头一起运动,就是
	要从最后一节开始,让蛇的每一节身体都随着移动到它前一节身体的位置上
	*/
	int i;
	for (i = snake.num - 1; i > 0; i--)  //因为是对数组进行操作,所以这里要 -1,并且这里是i-- 
	{
		snake.coor[i].x = snake.coor[i - 1].x;
		snake.coor[i].y = snake.coor[i - 1].y;

	}
	switch (snake.dir)
	{
	case UP:snake.coor[0].y -= 10;
		/*
		这里要实现的是可以在窗口处自由进出
		因为记录的是蛇头方块左边的坐标,而这里我们要使蛇头在接触到边沿(是蛇头的右边)就进入下面的边界
		所以这里要有个+10,就是为了去掉蛇头自身的长度
		*/
		if (snake.coor[0].y +10 <= 0) 
		{
			snake.coor[0].y = WIN_HEIGHT;
		}
		break; 
	case DOWN:snake.coor[0].y += 10;
		if (snake.coor[0].y  >= WIN_HEIGHT)
		{
			snake.coor[0].y = 0;
		}
		break;
	case LEFT:snake.coor[0].x -= 10;
		if (snake.coor[0].x + 10 <= 0)
		{
			snake.coor[0].x = WIN_WIDTH;
		}
		break;
	case RIGHT:snake.coor[0].x += 10; 
		if (snake.coor[0].x  >= WIN_WIDTH)
		{
			snake.coor[0].x = 0;
		}
		break;

	default:printf("错误操作,请重新操作!"); 
		break;
	}
	//snake.coor[0].x += 10;

}

这事,我们就已经可以对蛇进行各种操作了。下面我们就可以实现吃食物的功能了。

5.贪吃长大

在设计食物这里要注意的是很重要的一点——食物的坐标,由于食物的坐标是随机产生的值, 我们可以通过取模来让其范围在游戏区域内,但是:蛇头的大小是10*10,如果坐标不是十的倍数,蛇头是无法识别并继续的,所以,这里就需要一点操作,让其直接产生在游戏区域内的,十的倍数的,我们可以先让结果取10的商,由于是整数类型,就可以舍掉其他部分,只保留整数,再乘以10就可以产生我们需要的结果了

同时,我们也要保证食物在被蛇头接触之后就要消失,所以在这里我使用的是食物的 flag值,只在flag 为0时产生新的食物,下面是这个函数的实现代码:

void EatFood()
{
	//判断蛇头和食物的坐标就可以了
	if (snake.coor[0].x == food.fd.x && snake.coor[0].y == food.fd.y&&food.flag==1)
	{
		snake.num++;
		snake.score += 10;
		food.flag = 0;   //在吃完之后,就让食物不存在
	}
	if (food.flag == 0)  //当食物吃完之后就可以继续产生食物了
	{
		food.fd.x = rand() % (WIN_WIDTH / 10) * 10;  
		food.fd.y = rand() % (WIN_HEIGHT / 10) * 10;
		food.flag = 1; 
		food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
	}

}

6.判断游戏结束

下面我们就可以判断游戏何时结束了,因为是数组存储的信息,我们就只需要判断蛇头(数组首元素)和其他身体的值有没有相等的就可以得出结论了,同时要在游戏结束时显示分数等结束信息,下面是实现代码:

void DontEatSelf()
{
	for (int i = 4; i < snake.num; i++)
	{
		if (snake.coor[0].x == snake.coor[i].x && snake.coor[0].y == snake.coor[i].y)
		{
			outtextxy(200, 200, "Game over!");
			_getch();  //保持停顿
			exit(666);
		}

	}
}

好了,到现在这个函数的各个部分的信息就已经全都实现了,最后我们就只需要在mine函数汇总设置一个死循环就可以保证一直调用这些函数了,并且在循环里要设置延迟时间,来保证游戏是可以操作的,这里我使用的是Sleep()函数,关于这个函数的具体使用,我在这个博客中有详细介绍:动画效果—时间函数

其他的时间函数我还很不了解,等到日后又机会我再学习补充(^_−)☆

下面是main函数的调用代码:

void main(void)
{
	initgraph(WIN_WIDTH,WIN_HEIGHT,SHOWCONSOLE);//初始化一个图形窗口
	printf("\tWelcome! \n");
	printf("\tPlay Game Right Now!\n");
	GameInit();  //初始化游戏元素
	
	while (1)  //使用死循环来保证可以一直游玩
	{
		{
			SnakeMove();
			t1 = t2;
		}
		t2 = GetTickCount;*/

		SnakeMove();  //先移动
		GameDraw();  //再画蛇
		EatFood();
		KeyControl(); 
		DontEatSelf();
		Sleep(50);  //每次都要间隔半秒钟,才可以实时的看见,否则运行速度太快了,没法操作
		//同理这个操作也可以成为设置游戏难度的选项
		//Pause();

	}
	

	getchar();//用来防止 游戏窗口一闪而过
	closegraph();  //关闭游戏图形窗口
}

好了,到现在这个小游戏的完整效果就已经可以实现了,但是还有锦上添花的一点—— 背景音乐及音效

配乐:

这里由于我能力有限,就先只把背景音乐放上吧,这里需要两个特殊的头文件

#include <mmsyscom.h>//包含了可以播放音乐的头文件
#pragma comment(lib,"winmm.lib")//库文件

我选择在初始化那里就播放音乐(我用的是可商用的音乐素材(*^▽^*)),这里需要的函数是这两个:

mciSendString("open Carnival_De_Brazil.mp3 alias a",0,0,0);
	mciSendString("play a repeat",0,0,0);

如果需要其他音效的话,原理是类似的。

下面就是完整的实现代码了(包含背景音乐):

#define _CRT_SECURE_NO_WARNINGS 1
#undef UNICODE
#undef _UNICODE
/*
自己创建窗口
左上角为(0,0),横着为x轴,竖着为y轴

*/

#include <stdio.h>
#include <Windows.h>
#include <graphics.h>
#include <time.h>
#include <stdlib.h>

#include <mmsyscom.h>//包含了可以播放音乐的头文件
#pragma comment(lib,"winmm.lib")//库文件

#include <conio.h>

//用宏定义了窗口的显示宽高
#define WIN_WIDTH 640
#define WIN_HEIGHT 480
#define MAX_SNAKE 100  //蛇的最大节数

enum DIR
{
	UP,
	DOWN,
	LEFT,
	RIGHT,
	
};

struct Snake_tlg  //蛇的结构体
{
	int num;  // 身体的长度
	int dir;  //蛇的方向
	int score;  //分数
	int size;  //蛇的尺寸,身体和高度
	POINT coor[MAX_SNAKE];  //蛇的位置坐标。一个结构体数组
	//POINT 也时一个结构体,,可以通过选中转到定义来查看其具体结构,是在 windef.h中的
	/*
	typedef struct tagPOINT
	{
    LONG  x;
    LONG  y;
	} POINT, *PPOINT, NEAR *NPPOINT, FAR *LPPOINT;
	*/

}snake;

struct Food_tlg  //食物的出现 结构体
{
	POINT fd;  //食物的坐标
	int flag;  //是否已经被蛇吃掉
	DWORD color;  //定义食物的颜色
	/*在DirectX程序中有些地方用DWORD(RGBA)格式表示颜色,其范围是[0 - 255]*/
}food;

void GameInit();  //初始化游戏元素
void GameDraw();  //创建贪吃蛇
void SnakeMove();  // 操作蛇的移动
void KeyControl();  //控制方向函数
void EatFood();  //吃食物
void DontEatSelf();  //游戏结束判断
 void Pause();  //游戏暂停

void main(void)
{
	initgraph(WIN_WIDTH,WIN_HEIGHT,SHOWCONSOLE);//初始化一个图形窗口
	//具体参数见:https://docs.easyx.cn/zh-cn/initgraph
	printf("\tWelcome! \n");
	printf("\tPlay Game Right Now!\n");
	GameInit();  //初始化游戏元素

	while (1)  //使用死循环来保证可以一直游玩
	{
		

		SnakeMove();  //先移动
		GameDraw();  //再画蛇
		EatFood();
		KeyControl(); 
		DontEatSelf();
		Sleep(50);  //每次都要间隔半秒钟,才可以实时的看见,否则运行速度太快了,没法操作
		//同理这个操作也可以成为设置游戏难度的选项
		Pause();

	}
	
	getchar();//用来防止 游戏窗口一闪而过
	closegraph();  //关闭游戏图形窗口
}

void GameInit()
{
	srand((unsigned int)time(NULL));	//time 的头文件是 time.h

	//定义蛇
	snake.num = 3;  //定义蛇初始有三节身体
	snake.dir = RIGHT;  //开始时蛇头向右
	snake.score = 0;  //开始时分数为0
	snake.size = 10;  //初始化蛇的身体大小
	snake.coor[2].x = 0;  //第三节身体(尾巴)的坐标
	snake.coor[2].y = 0;  // 由于初始化时就是在左上角的一个长条,所以y都是0
	snake.coor[1].x = 0+snake.size;  // 蛇的第二节(身体的坐标
	snake.coor[1].y = 0;
	snake.coor[0].x = 0+2*snake.size;  //蛇头,即第一节身体
	snake.coor[0].y = 0;

	//初始化食物
	food.fd.x = rand() % (WIN_WIDTH/10)*10;  //取模确保产生的随机值在范围内
	food.fd.y = rand() % (WIN_HEIGHT/10)*10;
//这里要先对10取商,再乘10是为了产生一个10的倍数,可以让蛇头和食物接触到,而不是每次都是一个随机数,无法判断
	food.flag = 1;  //初始化食物存在
	food.color = RGB(rand() % 256, rand() % 256, rand() % 256);//随机产生食物颜色

	mciSendString("open Carnival_De_Brazil.mp3 alias a",0,0,0);
	mciSendString("play a repeat",0,0,0);

}


void GameDraw()
{
	setbkcolor(RGB(105, 160, 141));  //设置背景颜色,与web开发中的颜色设置一样
	cleardevice();  //清除其他元素

	//画蛇
	setfillcolor(YELLOW);
	int i;
	for (i = 0; i < snake.num; i++)  //每次都画一个矩形,共三次
	{
		setlinecolor(BLACK);
		fillrectangle(snake.coor[i].x,
			snake.coor[i].y,
			snake.coor[i].x + snake.size,
			snake.coor[i].y + snake.size);
		/*在 easyx 中,solidrectangle 这个函数用于画无边框的填充矩形,而fillrectangle用于画有边框的填充矩形
	void solidrectangle(int left,int top,int right,int bottom);
	void solidrectangle(矩形左部 x 坐标,	矩形顶部 y 坐标,	矩形右部 x 坐标,	矩形底部 y 坐标);
		*/

		//printf("%d %d\n", snake.coor[i].x, snake.coor[i].y);  //用于效验坐标,在调试时使用
	}

	//画食物
	if (food.flag == 1) //要确保在食物被吃完之后就消失不见
	{

		setfillcolor(food.color);  //设置食物颜色
		fillellipse(food.fd.x, food.fd.y, food.fd.x + 10, food.fd.y + 10);  //设置食物形状
	}

	//显示分数
	char temp[20] = "";
	sprintf(temp,"分数:%d", snake.score);
	//SetBkMode(TRANSPARENT);
	outtextxy(20, 20, temp);
	
}

void KeyControl()
{
	if (GetAsyncKeyState(VK_UP)&&snake.dir!=DOWN)//后面的条件确保了不可以进行反向操作
	{
		snake.dir = UP;
	}
	if (GetAsyncKeyState(VK_DOWN) && snake.dir != UP)
	{
		snake.dir = DOWN;
	}
	if (GetAsyncKeyState(VK_LEFT) && snake.dir != RIGHT)
	{
		snake.dir = LEFT;
	}
	if (GetAsyncKeyState(VK_RIGHT) && snake.dir != LEFT)
	{
		snake.dir = RIGHT;
	}

}

void SnakeMove()
{
	/*要操作蛇头的移动,蛇的身体随着蛇头一起运动,就是
	要从最后一节开始,让蛇的每一节身体都随着移动到它前一节身体的位置上
	*/
	int i;
	for (i = snake.num - 1; i > 0; i--)  //因为是对数组进行操作,所以这里要 -1,并且这里是i-- 
	{
		snake.coor[i].x = snake.coor[i - 1].x;
		snake.coor[i].y = snake.coor[i - 1].y;

	}
	switch (snake.dir)
	{
	case UP:snake.coor[0].y -= 10;
		/*
		这里要实现的是可以在窗口处自由进出
		因为记录的是蛇头方块左边的坐标,而这里我们要使蛇头在接触到边沿(是蛇头的右边)就进入下面的边界
		所以这里要有个+10,就是为了去掉蛇头自身的长度
		*/
		if (snake.coor[0].y +10 <= 0) 
		{
			snake.coor[0].y = WIN_HEIGHT;
		}
		break; 
	case DOWN:snake.coor[0].y += 10;
		if (snake.coor[0].y  >= WIN_HEIGHT)
		{
			snake.coor[0].y = 0;
		}
		break;
	case LEFT:snake.coor[0].x -= 10;
		if (snake.coor[0].x + 10 <= 0)
		{
			snake.coor[0].x = WIN_WIDTH;
		}
		break;
	case RIGHT:snake.coor[0].x += 10; 
		if (snake.coor[0].x  >= WIN_WIDTH)
		{
			snake.coor[0].x = 0;
		}
		break;

	default:printf("错误操作,请重新操作!"); 
		break;
	}
	//snake.coor[0].x += 10;

}

void EatFood()
{
	//判断蛇头和食物的坐标就可以了
	if (snake.coor[0].x == food.fd.x && snake.coor[0].y == food.fd.y&&food.flag==1)
	{
		snake.num++;
		snake.score += 10;
		food.flag = 0;   //在吃完之后,就让食物不存在
	}
	if (food.flag == 0)  //当食物吃完之后就可以继续产生食物了
	{
		food.fd.x = rand() % (WIN_WIDTH / 10) * 10;  
		food.fd.y = rand() % (WIN_HEIGHT / 10) * 10;
		food.flag = 1; 
		food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
	}

}

void DontEatSelf()
{
	for (int i = 4; i < snake.num; i++)
	{
		if (snake.coor[0].x == snake.coor[i].x && snake.coor[0].y == snake.coor[i].y)
		{
			outtextxy(200, 200, "Game over!");
			_getch();  //保持停顿
			exit(666);
		}

	}
}

void Pause()
{
	if (_getch() == 32)  //32表示空格
	{
		//while (_getch() != ' ');
		while (1)
		{
			_getch();
		}
	}
	
}


完成!!ヾ(๑╹◡╹)ノ”



总结

好了,到现在就完整的实现了贪吃蛇的游戏效果,通过这个程序的编程实现,对于图形化操作界面、数组的深入、游戏逻辑的判断等等都有了更深入的了解,对于编程的提高有很大的帮助,当然了,这个程序现在也还有遗憾的地方,就是不能在任意时刻停止,我会继续更新然后实现这个更完美的效果的!(>ω・* )ノ

感谢观赏,一起提高,慢慢变强。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/37770.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注