贪吃蛇—C语言控制台简单实现

模块拆分

蛇身及蛇身打印

这里我采用的是结构体数组来储存蛇身信息,包含x,y坐标。当然也可以用其他的储存方式,比如二维数组、链表等。“场地”的大小是24x24,用字符*来表示蛇身

struct snake {
	int x, y;
};//蛇身信息
struct snake body[600];//结构体数组表示蛇身

	body[0].x = 2; body[0].y = 0;
	body[1].x = 1; body[1].y = 0;
	body[2].x = 0; body[2].y = 0; //初始化,初始长度为3
	//此处注意蛇头的信息一定要存在body[0]
	void show()
{
	int is_body=0; //判断是否是是否是蛇身
	system("cls");  //清屏函数,在windows.h里
	for (i = 0; i < 24; i++)
	{
		for (j = 0; j < 24; j++)
		{
			for(k=0;k<lenth;k++) //遍历目前蛇身长度
				if (i == body[k].y && j == body[k].x||i== target _y&&j== target_x) 
					is_body = 1;//判断是否是蛇身或目标(后面随机生成)
					//注意这里i是行号代表y,j是列号代表x
			if(is_body ==1) printf("*");
			else printf(" ");
			is_body = 0;
		} 
		printf("|\n");//每行的末尾打印边界
	}
	for (i = 0; i < 24; i++)
		printf("-"); //最后一行打印边界
}

运行效果
在这里插入图片描述
因为字符是长方形的,最后打印出来也是长方形的,原理就是一行行打印,在遇到对应蛇身的x,y坐标的时候就打印星号。注意清屏函数的使用。

蛇身的移动(重点)

当我们移动蛇身时,我们只需要改变蛇身的x,y坐标。因为蛇是连续的,我们可以这样理解,当我们移动的时候,每一步我们改变的只有头和尾,我们将除了头以外的身体部分,覆盖之前除了尾的身体部分,这样得到的就是连续变化的了。再改变头的坐标,往要移动的方向前进一格。这一部分理解了,贪吃蛇就完成了一大半。
那么我们怎么从键盘获得输入,从而改变方向呢。这里需要用到 _kbhit() 函数(在<conio.h>下),检查当前是否有键盘输入,若有则返回一个非0值,否则返回0。再用_getch()函数得到用户键入的字符,若没有输入,既保持这个方向移动不变。
还要用到延时函数Sleep(微秒数)(在<windows.h>里),来减慢移动速度。

void move()
{	
	if (a == 'w') {
		for (i = lenth - 1; i > 0; i--)
			body[i] = body[i - 1]; //移动除蛇头部分
			body[0].y -= 1; //将蛇头向上移动一格
	}
	if (a == 's') {
		for (i = lenth - 1; i > 0; i--)
			body[i] = body[i - 1]; 
			body[0].y += 1; //向下运动
	}
	if (a == 'a') {
		for (i = lenth - 1; i > 0; i--)
			body[i] = body[i - 1]; 
			body[0].x -= 1;//向左运动
	}
	if (a == 'd') {
		for (i = lenth - 1; i > 0; i--)
			body[i] = body[i - 1]; 
			body[0].x += 1;//向右运动
	}
	show();
}
	//main函数中的内容,die_flag,用来判断是否死亡
	while (die_flag)
	{
		if (_kbhit())
		{
			a = _getch(); move(); Sleep(speed); //有键入时将字符赋给a
		}
		else {
			move(); Sleep(speed);//没有键入,a不变继续此方向移动
	}

判定死亡

游戏结束有两种情况,一种是碰到边界了,另一种是碰到自身了,判定的方法都是将蛇头的x,y坐标和边界和自身其他x,y相比较。

void die()
{
	for (i = 1; i < lenth; i++)
		if (body[i].x == body[0].x && body[i].y == body[0].y || body[0].x > 23 || body[0].x < 0 || body[0].y >23|| body[0].y < 0)die_flag = 0;
		//遍历蛇身,看蛇头是否有重合,及是否超出边界
	if (die_flag == 0) {
		system("cls");
		printf("GAME OVER!");
	}
}
	while (die_flag)
	{
		if (_kbhit())
		{

			a = _getch(); move();die(); Sleep(speed);
		}
		else {
			move();  die(); Sleep(speed);  //每次移动后都判定是否死亡
		}
	}

生成目标

生成目标我们只需要生成两个小于24的随机数,作为目标的横纵坐标,srand函数在stdlib.h头文件中,srand函数生成的是伪随机数,所以还需要播种,我们将时间转换成数字,这样就能保证每一次的种子不同。
另外,我们生成的目标还不能与蛇身重合,否则玩家会找不到目标。

void target()
{
	is_on_body = 1; //判断目标是否生成在蛇身上
	srand((unsigned)time(NULL));
	target_x = (rand() % 24) ;
	target_y = (rand() % 24) ; //生成随机数
	for (i = 0; i < lenth; i++)
		if (body[i].x == target_x && body[i].y == target_y) is_on_body = 0;//判断是否和蛇身重合
}
}
	//生成目标的时候用do while,首先执行一次,如果重合就继续执行
	do
	target(); while (is_on_body == 0);

吃到目标后加长

加长无非是在蛇尾后加一个,但是这一块怎么保证和蛇身方向相同呢。其实不难,我们能够轻易得知,倒数第二块和倒数第一块总是在一个方向上。那么我新加上的这一块和倒数第一块的相对位置也是如此。

void add()
{
	if (body[0].x == target_x && body[0].y == target_y)
	{
		lenth++;//蛇身长度加一
		body[lenth - 1].x = 2 * body[lenth - 2].x - body[lenth - 3].x;
		body[lenth - 1].y = 2 * body[lenth - 2].y - body[lenth - 3].y;
		//计算添加的蛇尾的位置
		show();
		if (lenth % 5 == 0 && speed != 100)speed -= 50;//增加游戏难度,每吃到五个目标速度加快50ms
		do
			target(); while (is_on_body == 0);//吃到目标后重新生成目标
	}
}

模块整合

最后将几个函数有序地放在一起,就完成了整个贪吃蛇。

	while (die_flag)
	{
		if (_kbhit())
		{

			a = _getch(); move(); add(); die(); Sleep(speed);
		}
		else {
			move(); add(); die(); Sleep(speed);
		}

不过控制台玩起来十分不流畅,主要是刷新率的问题,还有光标,其实也有办法解决,控制台输出的时候,光标不一定要顺序移动,我们可以用SetConsoleCursorPosition()函数直接将光标移动到相应位置,还可以用HideCursor()来隐藏光标
这里我们就不做过多的讲解了,后面我们用easyx可以做出更流畅,用户体验更好的版本。

完整代码

#include<stdio.h>
#include<stdlib.h>
#include <windows.h>
#include<conio.h>
#include <time.h>
void move();
void add();
void die();
void target();
void show();
int i, j,k, target_x, target_y, speed, lenth, die_flag, is_on_body;
char a;
struct snake {
	int x, y;
};//蛇身信息
struct snake body[600];//结构体数组表示蛇身
int main()
{
	int c = 1;
	speed = 300;
	lenth = 3; die_flag = 1;


	body[0].x = 2; body[0].y = 0;
	body[1].x = 1; body[1].y = 0;
	body[2].x = 0; body[2].y = 0; //初始化,初始长度为3
	//此处注意蛇头的信息一定要存在body[0]
	show();
	do
	target(); while (is_on_body == 0);
	while (die_flag)
	{
		if (_kbhit())
		{

			a = _getch(); move(); add(); die(); Sleep(speed);
		}
		else {
			move(); add(); die(); Sleep(speed);
		}
	}
	return 0;
}
void move()
{	
	if (a == 'w') {
		for (i = lenth - 1; i > 0; i--)
			body[i] = body[i - 1]; body[0].y -= 1; //向上运动
	}
	if (a == 's') {
		for (i = lenth - 1; i > 0; i--)
			body[i] = body[i - 1]; body[0].y += 1; //向下运动
	}
	if (a == 'a') {
		for (i = lenth - 1; i > 0; i--)
			body[i] = body[i - 1]; body[0].x -= 1;//向左运动
	}
	if (a == 'd') {
		for (i = lenth - 1; i > 0; i--)
			body[i] = body[i - 1]; body[0].x += 1;//向右运动
	}
	show();
}
void show()
{
	int is_body=0;
	system("cls");  //清屏函数,在windows.h里
	for (i = 0; i < 24; i++)
	{
		for (j = 0; j < 24; j++)
		{
			for(k=0;k<lenth;k++)  //遍历目前蛇身长度
				if (i == body[k].y && j == body[k].x||i== target_y&&j== target_x) 
					is_body = 1;//判断是否是蛇身或目标(后面随机生成)
					//注意这里i是行号代表y,j是列号代表
			if(is_body ==1) printf("*");
			else printf(" ");
			is_body = 0;
		}
		printf("|\n");//每行的末尾打印边界
	}
	for (i = 0; i < 24; i++)
		printf("-");//最后一行打印边界
}
void add()
{
	if (body[0].x == target_x && body[0].y == target_y)
	{
		lenth++;
		body[lenth - 1].x = 2 * body[lenth - 2].x - body[lenth - 3].x;
		body[lenth - 1].y = 2 * body[lenth - 2].y - body[lenth - 3].y;
		show();
		if (lenth % 5 == 0 && speed != 100)speed -= 50;
		do
			target(); while (is_on_body == 0);
	}
}
void die()
{
	for (i = 1; i < lenth; i++)
		if (body[i].x == body[0].x && body[i].y == body[0].y || body[0].x > 23 || body[0].x < 0 || body[0].y >23|| body[0].y < 0)die_flag = 0;
		//遍历蛇身,看蛇头是否有重合,及是否超出边界
	if (die_flag == 0) {
		system("cls");
		printf("GAME OVER!");
	}
}
void target()
{
	is_on_body = 1;
	srand((unsigned)time(NULL));
	target_x = (rand() % 24) ;
	target_y = (rand() % 24) ;//生成随机数
	for (i = 0; i < lenth; i++)
		if (body[i].x == target_x && body[i].y == target_y) is_on_body = 0; //判断是否和蛇身重合
}
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值