C语言实现彩色版贪吃蛇(详细代码及思路讲解)

版权声明:本文为博主原创文章,转载请注明来源 https://blog.csdn.net/C1664510416/article/details/82558690

       本人是机械专业学生,学完C语言后就考了一个二级,没有像软件学院那样有一个实习,所以决定做几个项目锻炼一下自己,这个贪吃蛇是最简单的一个项目。

       我原先在网上查看了一些做好的贪吃蛇,可以运行起来,但是做的很粗糙,而那些界面精美一点的都用到了图形库,我觉得太麻烦了。这个代码用到了一点Windows编程的东西,没有用到图形库,实现起来也不复杂,界面也说的过去,如下图:

下面是这个程序的源码(写了7个多小时),具体的函数有哪些作用下面都有:

#include <stdio.h>
#include <windows.h>
#include <conio.h>
#include <malloc.h>
#include <time.h>
#include <stdlib.h>
void setmouth(int x, int y);//设置光标
void background();//背景生成
void setcolour(int a);//设置颜色
void init();//初始化
void hidemouth();//隐藏光标
void startfood();//初始创建食物
void startsnack();//初始创建小蛇
void snackmove(int,int);//小蛇移动
int checksnack(int);//小蛇碰撞检测
void snackdie();//小蛇死亡
void showing();//输出最新的show
void screenRefresh(int);//屏幕刷新
void newfood();//创建新食物
void updategoal();//更新得分
int movedire(int,int);//获取小蛇的移动方向
int snacklength = 3;//小蛇长度
int snackhead=2;//蛇头
int snacktail=0;//蛇尾
int MaxLine = 40, MaxLie = 40;//定义最大行数,列数
//结构体保存各个事物的属性
struct map {
	int colour;
	int kind;
	char *display;
}
show[40][40], //展示地图
wall = { 0x6C,1,"■" },//墙体
livearea = { 0x7F,2,"□" },//小蛇活动区域
food = { 0x7C,3,"★" },//食物
snack = { 0x7A,4,"●" };//小蛇蛇体的属性

struct SnackMap
{
	int x;
	int y;
	
}SnackSite[100],FoodSite;

/***************************************
                主程序
****************************************/

int main()
{
	int olddirection='w'; //初始键盘信号
	int newdirection =0;//获取键盘信号
	init();//初始化

	setmouth(60, 0);
	setcolour(0xF0);
	printf("\n得分:");
	printf("%d\n", snacklength - 3);
	printf("请切换为英文输入法\n");
	printf("W A S D键分别表示:上 左 下 右\n");
	printf("      Copyright [2018.9.9] by [松鼠君]        ");
	
	while (1) {
		if (_kbhit()) {  //检测缓冲区中是否有数据
			newdirection = tolower(_getch());  //将缓冲区中的数据以字符的形式读出
			if (newdirection == 27) {
				break;
			}
			else {
				//获取小蛇新的移动方向
				newdirection=movedire(olddirection, newdirection);
				if (newdirection != olddirection)
				{
					olddirection = newdirection;
				}
			}
		}
		screenRefresh(newdirection);
		Sleep(10);
	}
	return 0;
}
//更新得分
void updategoal()
{
	setcolour(0xF0);
	setmouth(60, 0);
	printf("\n得分:%d", snacklength - 3);
}
//碰撞检测
int checksnack(int newdirection)
{
	switch (newdirection)
	{
	case 'w':
		if ((show[SnackSite[snackhead].x - 1][SnackSite[snackhead].y].kind)==1)
		{
			snackdie();
			return 0;
		}else if((show[SnackSite[snackhead].x - 1][SnackSite[snackhead].y].kind) == 4)
		{
			snackdie();
			return 0;
		}
		else if ((show[SnackSite[snackhead].x - 1][SnackSite[snackhead].y].kind) == 3)
		{
			return 1;
		}
		else {
			return 2;

		}
		break;
	case 's':
		if ((show[SnackSite[snackhead].x + 1][SnackSite[snackhead].y].kind) == 1)
		{
			snackdie();
			return 0;
		}
		else if ((show[SnackSite[snackhead].x + 1][SnackSite[snackhead].y].kind) == 4)
		{
			snackdie();
			return 0;
		}
		else if ((show[SnackSite[snackhead].x + 1][SnackSite[snackhead].y].kind) == 3)
		{
			return 1;
		}
		else {
			return 2;
		}
		break;
	case 'a':
		if ((show[SnackSite[snackhead].x][SnackSite[snackhead].y-1].kind) == 1)
		{
			snackdie();
			return 0;
		}
		else if ((show[SnackSite[snackhead].x][SnackSite[snackhead].y-1].kind) == 4)
		{
			snackdie();
			return 0;
		}
		else if ((show[SnackSite[snackhead].x][SnackSite[snackhead].y-1].kind) == 3)
		{
			return 1;
		}
		else {
			return 2;
		}
		break;
	case 'd':
		if ((show[SnackSite[snackhead].x][SnackSite[snackhead].y+1].kind) == 1)
		{
			snackdie();
			return 0;
		}
		else if ((show[SnackSite[snackhead].x][SnackSite[snackhead].y+1].kind) == 4)
		{
			snackdie();
			return 0;
		}
		else if ((show[SnackSite[snackhead].x][SnackSite[snackhead].y+1].kind) == 3)
		{
			return 1;
		}
		else {
			return 2;
		}
		break;

	}
}

//创建新食物
void newfood()
{
	srand((unsigned)time(NULL));
	FoodSite.x = rand() % 38 + 1;
	FoodSite.y = rand() % 38 + 1;
}

//刷新屏幕
void screenRefresh(int newdirection)
{
	int re = 0;
	int i = 0;
	re = checksnack(newdirection);
	snackmove(newdirection, re);
	background();
	show[FoodSite.x][FoodSite.y] = food;
	for (i = 0; i <= snackhead; i++)
	{
		if (i == snackhead) {
			show[SnackSite[i].x][SnackSite[i].y] = snack;
			show[SnackSite[i].x][SnackSite[i].y].colour = 0x75;

		}
		else {
			show[SnackSite[i].x][SnackSite[i].y] = snack;
		}
		
	}
	
	showing();
	if (re == 1)
	{
		snacklength++;
		updategoal();
		newfood();
	}


}
/*********************************************
				获取最新的方向
**********************************************/
int movedire(int olddirection, int newdirection)
{
	if (newdirection == 'w' || newdirection == 'a' || newdirection == 's' || newdirection == 'd')
	{

		return newdirection;
	}
	else {
		return olddirection;
	}

}

/**********************************
          展示最新的show
*********************************/

void showing()
{
	int line, lie;
	for (line = 0; line < MaxLine; line++)
	{
		for (lie = 0; lie < MaxLie; lie++)
		{
			setmouth(line, lie);
			setcolour(show[line][lie].colour);
			printf("%s",show[line][lie].display);

		}
	}

}

/******************************************
			小蛇死亡
*******************************************/


void snackdie()
{
	setmouth(20, 5);
	printf("Game over! 按任意键结束游戏!");
	Sleep(5000);
	_getch();
	exit(0);
}

//小蛇移动
void snackmove(int newdirection,int re)
{
	int i = 0;
	
	switch (newdirection)
	{
	case 'w':
		for (i = 0; i < snackhead;i++) {
			SnackSite[i].x = SnackSite[i + 1].x;
			SnackSite[i].y = SnackSite[i + 1].y;
		}
		SnackSite[snackhead].x -= 1;
		if (re == 1)
		{
			snackhead += 1;
			SnackSite[snackhead].x = SnackSite[snackhead-1].x-1;
			SnackSite[snackhead].y = SnackSite[snackhead - 1].y;

		}
		break;
	case 's':
		for (i = 0; i < snackhead; i++) {
			SnackSite[i].x = SnackSite[i + 1].x;
			SnackSite[i].y = SnackSite[i + 1].y;
		}
		SnackSite[snackhead].x += 1;
		if (re == 1)
		{
			snackhead += 1;
			SnackSite[snackhead].x = SnackSite[snackhead - 1].x + 1;
			SnackSite[snackhead].y = SnackSite[snackhead - 1].y;

		}
		break;
	case 'a':
		for (i = 0; i < snackhead; i++) {
			SnackSite[i].x = SnackSite[i + 1].x;
			SnackSite[i].y = SnackSite[i + 1].y;
		}
		SnackSite[snackhead].y -= 1;
		if (re == 1)
		{
			snackhead += 1;
			SnackSite[snackhead].x = SnackSite[snackhead - 1].x ;
			SnackSite[snackhead].y = SnackSite[snackhead - 1].y-1;

		}
		break;
	case 'd':
		for (i = 0; i < snackhead; i++) {
			SnackSite[i].x = SnackSite[i + 1].x;
			SnackSite[i].y = SnackSite[i + 1].y;
		}
		SnackSite[snackhead].y += 1;
		if (re == 1)
		{
			snackhead += 1;
			SnackSite[snackhead].x = SnackSite[snackhead - 1].x;
			SnackSite[snackhead].y = SnackSite[snackhead - 1].y+1;

		}
		break;

	}

}



//初始食物


void startfood()
{
	FoodSite.x = 5;
	FoodSite.y = 10;
	show[5][10] = food;
}



//初始小蛇

void startsnack()
{
	SnackSite[0].x = 35;
	SnackSite[0].y = 13;
	SnackSite[1].x = 35;
	SnackSite[1].y = 12;
	SnackSite[2].x = 35;
	SnackSite[2].y = 11;
	show[35][11] = snack;
	show[35][11].colour = 0x75;
	show[35][12] = snack;
	show[35][13] = snack;
}

/*******************************
	初始化生成show结构体数组
*******************************/

void init()
{
	//设置窗口大小
	system("mode con:cols=140 lines=50");
	//隐藏光标
	hidemouth();
	//生成背景图
	background();
	//创建初始食物
	startfood();
	//创建初始小蛇蛇体
	startsnack();
	//显示
	showing();
}


/**************************************
			生成背景图
**************************************/
void background()
{
	int line, lie;
	for (line = 0; line < MaxLine; line++)
	{
		for (lie = 0; lie < MaxLie; lie++)
		{
			setmouth(line, lie);
			if (lie == 0 || lie == MaxLie-1 || line == 0 || line == MaxLine-1) {
				show[line][lie] = wall;
			}
			else {
				show[line][lie] = livearea;
			}
			
		}
	}
}

/********************************
		定义光标位置
*********************************/

void setmouth(int x, int y)
{
	COORD coord;
	coord.X = y*2;  //第3列
	coord.Y = x;  //第3行
	//获取控制台缓冲区句柄
	HANDLE ConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
	//设置光标位置
	SetConsoleCursorPosition(ConsoleHandle, coord);
}

//设置种类的颜色
void setcolour(int a)
{
		HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
		SetConsoleTextAttribute(hConsole, a);	
}


//隐藏光标
void hidemouth()
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(handle, &CursorInfo);//获取控制台光标信息
	CursorInfo.bVisible = 0; //隐藏控制台光标
	SetConsoleCursorInfo(handle, &CursorInfo);//设置控制台光标状态

}

下面是上面程序的大体流程图:

本程序的思路是这样的,我们每次刷新屏幕都会重新生成一张图片,也就是show结构体数组,每个元素都包含了一个“像素点”的信息,这个信息是用结构体 map来定义的,包含了像素点的属性(种类(食物,小蛇蛇体,墙体,可活动范围),颜色(colour)和输出图形(char))。将每次生成的新的show输出,就形成了小蛇的移动。

难点:

         我在写程序的时候有一个问题,想了很久,就是小蛇的生长和移动到底怎么来实现?上面我们想到用像素点的方式来生成画面,从这个思路入手进一步发挥。我们知道小蛇每次移动一格(即屏幕刷新一次),就相当于头节点向控制方向前进一个位置,并且删除尾巴节点。小蛇生长,就是在小蛇移动过后,再在头节点前加一个节点。本来我准备用链表来做,可是实现起来太复杂了,后来晚上洗澡的时候想到一个办法。我用数组来做,把蛇的节点的位置坐标倒序存进一个数组,每次移动就让尾节点等于前一个节点的坐标,到头节点,就直接向控制的方向增加就好。生成新show的时候,将这个数组的像素点定义称蛇体,再播放就形成了小蛇的移动。生长,就在数组尾部再增加一个节点,生成像素点会被加上去。这个过程在实现的时候是最难的,想了好久才想到的,蛇体位置点倒序存放在数组里。

       还有关于windows 的东西,下面链接里面都有:

https://blog.csdn.net/C1664510416/article/details/82559357

https://blog.csdn.net/C1664510416/article/details/82559395

https://blog.csdn.net/C1664510416/article/details/82559419

https://blog.csdn.net/C1664510416/article/details/82559440

       其他没有什么好说的了,看代码就能看懂。

猜你喜欢

转载自blog.csdn.net/C1664510416/article/details/82558690