C语言 10.贪吃蛇实现

1.实现要素

  1. 定义蛇对象、食物对象
  2. 初始化蛇、初始化食物
  3. 控制流程:
    1) 蛇头和墙壁的碰撞
    2) 蛇头和蛇身体的碰撞
    3) 蛇头和食物的碰撞
    1> 蛇身增长
    2> 食物消失 – 新食物产生
    3> 分数累加
    4> 移动速度增大
    4) 蛇的移动
        自动移动
        手动控制移动: ASWD — 左下上右
    5)显示分数
        排行榜
  4. 图形界面。

2. 实现

2.1 snake.h

#include "snake.h"

void initSnake(void)
{
	snake.size = 2;
	// 蛇头
	snake.body[0].X = WIDTH / 2;         // 每一个body[i]代表一个body块,蛇头是第一个body块
	snake.body[0].Y = HIGH / 2;
	// 蛇身
	snake.body[1].X = WIDTH / 2 - 1;         // 每一个body[i]代表一个body块,蛇头是第一个body块
	snake.body[1].Y = HIGH / 2;

	return;
}

void initFood(void)
{
	food.X = rand() % WIDTH;               // 0-59
	food.Y = rand() % HIGH;                // 0-19
	return;
}

// 初始化界面控件
void initUI(void)
{
	// 画蛇
	COORD coord={0};            //  光标移动的位置                        // 是一个结构体,有两个成员变量x和y
	
	for (size_t i = 0; i < snake.size; i++)
	{
		coord.X = snake.body[i].X;
		coord.Y = snake.body[i].Y;

		//setConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);         // 改变光标位置到初始化好的蛇头位置
		SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);           // 这里是大写的Set,不是小写的Set
		if (i)
			putchar('*');
		else
			putchar('@');
		/*if (i == 0)
			putchar('@');
		else
			putchar('*');*/
	}

	// 去除蛇尾
	coord.X = lastX;
	coord.Y = lastY;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
	putchar(' ');
	

	// 当不定义食物和写下面这句话时,打印初始化蛇只会出来一个*,这是因为打印结束出现的字覆盖了蛇头的数字。当打印完蛇身再
	// 把光标置为0,就可以避免这种情况
	//coord.X = 0;
	//coord.Y = 0;
	//SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);           // 这里是大写的Set,不是小写的Set


	// 画食物
	coord.X = food.X;
	coord.Y = food.Y;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
	putchar('#');

	 将光标默认位置移动至不干扰游戏的任意位置
	//coord.X = 0;
	//coord.Y = HIGH + 10;
	//SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);           // 这里是大写的Set,不是小写的Set
}

void initWall(void)
{
	for (size_t i = 0; i <= HIGH; i++)
	{
		for (size_t j = 0; j <= WIDTH; j++)
		{
			if (j == WIDTH)
			{
				printf("||");
			}
			else if (i == HIGH)
			{
				printf("=");
			}
			else
			{
				printf(" ");
			}
		}
		printf("\n");
	}
}
void playGame(void)
{
	char key = 'w';

	// 判断蛇撞墙
	while (snake.body[0].X >= 0 && snake.body[0].X < WIDTH && snake.body[0].Y >= 0 && snake.body[0].Y < HIGH)
	{
		
		//更新蛇
		initUI();
		//接收按键输入 ASDW  
		if (_kbhit()) {               // 为真时,说明用户有输入
			key = _getch();  
		}

		switch (key)
		{
		case 'w':key_x = 0; key_y = -1; break;          // 向上
		case 's':key_x = 0; key_y = +1; break;          // 向下
		case 'd':key_x = +1; key_y = 0; break;          // 向右
		case 'a':key_x = -1; key_y = 0; break;          // 向左
		default:
			break;
		}


		// 蛇头撞身体:蛇头==任意一节身体
		for (size_t i = 1; i < snake.size; i++)   // 这里蛇身体从1开始,不是从0开始
		{
			if (snake.body[0].X == snake.body[i].X && snake.body[0].Y == snake.body[i].Y)
			{
				return;         // 游戏结束
			}

		}

		// 蛇头撞食物
		if (snake.body[0].X == food.X && snake.body[0].Y == food.Y)
		{
			// 食物消失
			initFood();

			// 身体变长
			snake.size++;

			// 分数增长
			score+=10;

			//加速
			sleepSecond -= 50;
		}

		// 存储蛇尾坐标
		lastX = snake.body[snake.size - 1].X;
		lastY = snake.body[snake.size - 1].Y;

		// 蛇移动,前一节身体给后一节身体赋值
		for (size_t  i = snake.size-1;i>0; i--)
		{
			/*snake.body[i].X = snake.body[i-1].X;*/
			snake.body[i].X = snake.body[i - 1].X;
			snake.body[i].Y = snake.body[i - 1].Y;
		}
		snake.body[0].X += key_x;
		snake.body[0].Y += key_y;

		Sleep(sleepSecond);          // 这里的s是大写不然会报错

		 清频操作  这里加上清屏操作是为了去除尾巴,但是加上这个操作屏幕会一直闪,所以去掉,单独加一个去掉尾巴的操作。
		//system("cls");
	}
	return;

}

// 打印分数
void showScore(void)
{
	// 将光标默认位置移动至不干扰游戏的任意位置
	COORD coord;
	coord.X = 0;
	coord.Y = HIGH + 2;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);

	printf("GAME OVER!!!\n");
	printf("成绩为:%d\n", score);

}

int main(void)
{
	// 去除光标
	CONSOLE_CURSOR_INFO cci;
	cci.dwSize = sizeof(cci);
	cci.bVisible = FALSE;
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cci);

	srand(time(NULL));                     // 播种随机数种子,为了蛇身每次出现在不同的位置

	initSnake();
	initFood();
	initWall();                            // 画墙体
	initUI();                             // 画蛇和食物
	playGame();
	showScore();
	system("pause");
}

2.2 snake.c

#include "snake.h"

void initSnake(void)
{
	snake.size = 2;
	// 蛇头
	snake.body[0].X = WIDTH / 2;         // 每一个body[i]代表一个body块,蛇头是第一个body块
	snake.body[0].Y = HIGH / 2;
	// 蛇身
	snake.body[1].X = WIDTH / 2 - 1;         // 每一个body[i]代表一个body块,蛇头是第一个body块
	snake.body[1].Y = HIGH / 2;

	return;
}

void initFood(void)
{
	food.X = rand() % WIDTH;               // 0-59
	food.Y = rand() % HIGH;                // 0-19
	return;
}

// 初始化界面控件
void initUI(void)
{
	// 画蛇
	COORD coord={0};            //  光标移动的位置                        // 是一个结构体,有两个成员变量x和y
	
	for (size_t i = 0; i < snake.size; i++)
	{
		coord.X = snake.body[i].X;
		coord.Y = snake.body[i].Y;

		//setConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);         // 改变光标位置到初始化好的蛇头位置
		SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);           // 这里是大写的Set,不是小写的Set
		if (i)
			putchar('*');
		else
			putchar('@');
		/*if (i == 0)
			putchar('@');
		else
			putchar('*');*/
	}

	// 去除蛇尾
	coord.X = lastX;
	coord.Y = lastY;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
	putchar(' ');
	

	// 当不定义食物和写下面这句话时,打印初始化蛇只会出来一个*,这是因为打印结束出现的字覆盖了蛇头的数字。当打印完蛇身再
	// 把光标置为0,就可以避免这种情况
	//coord.X = 0;
	//coord.Y = 0;
	//SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);           // 这里是大写的Set,不是小写的Set


	// 画食物
	coord.X = food.X;
	coord.Y = food.Y;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
	putchar('#');

	 将光标默认位置移动至不干扰游戏的任意位置
	//coord.X = 0;
	//coord.Y = HIGH + 10;
	//SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);           // 这里是大写的Set,不是小写的Set
}

void initWall(void)
{
	for (size_t i = 0; i <= HIGH; i++)
	{
		for (size_t j = 0; j <= WIDTH; j++)
		{
			if (j == WIDTH)
			{
				printf("||");
			}
			else if (i == HIGH)
			{
				printf("=");
			}
			else
			{
				printf(" ");
			}
		}
		printf("\n");
	}
}
void playGame(void)
{
	char key = 'w';

	// 判断蛇撞墙
	while (snake.body[0].X >= 0 && snake.body[0].X < WIDTH && snake.body[0].Y >= 0 && snake.body[0].Y < HIGH)
	{
		
		//更新蛇
		initUI();
		//接收按键输入 ASDW  
		if (_kbhit()) {               // 为真时,说明用户有输入
			key = _getch();  
		}

		switch (key)
		{
		case 'w':key_x = 0; key_y = -1; break;          // 向上
		case 's':key_x = 0; key_y = +1; break;          // 向下
		case 'd':key_x = +1; key_y = 0; break;          // 向右
		case 'a':key_x = -1; key_y = 0; break;          // 向左
		default:
			break;
		}


		// 蛇头撞身体:蛇头==任意一节身体
		for (size_t i = 1; i < snake.size; i++)   // 这里蛇身体从1开始,不是从0开始
		{
			if (snake.body[0].X == snake.body[i].X && snake.body[0].Y == snake.body[i].Y)
			{
				return;         // 游戏结束
			}

		}

		// 蛇头撞食物
		if (snake.body[0].X == food.X && snake.body[0].Y == food.Y)
		{
			// 食物消失
			initFood();

			// 身体变长
			snake.size++;

			// 分数增长
			score+=10;

			//加速
			sleepSecond -= 50;
		}

		// 存储蛇尾坐标
		lastX = snake.body[snake.size - 1].X;
		lastY = snake.body[snake.size - 1].Y;

		// 蛇移动,前一节身体给后一节身体赋值
		for (size_t  i = snake.size-1;i>0; i--)
		{
			/*snake.body[i].X = snake.body[i-1].X;*/
			snake.body[i].X = snake.body[i - 1].X;
			snake.body[i].Y = snake.body[i - 1].Y;
		}
		snake.body[0].X += key_x;
		snake.body[0].Y += key_y;

		Sleep(sleepSecond);          // 这里的s是大写不然会报错

		 清频操作  这里加上清屏操作是为了去除尾巴,但是加上这个操作屏幕会一直闪,所以去掉,单独加一个去掉尾巴的操作。
		//system("cls");
	}
	return;

}

// 打印分数
void showScore(void)
{
	// 将光标默认位置移动至不干扰游戏的任意位置
	COORD coord;
	coord.X = 0;
	coord.Y = HIGH + 2;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);

	printf("GAME OVER!!!\n");
	printf("成绩为:%d\n", score);

}

int main(void)
{
	// 去除光标
	CONSOLE_CURSOR_INFO cci;
	cci.dwSize = sizeof(cci);
	cci.bVisible = FALSE;
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cci);

	srand(time(NULL));                     // 播种随机数种子,为了蛇身每次出现在不同的位置

	initSnake();
	initFood();
	initWall();                            // 画墙体
	initUI();                             // 画蛇和食物
	playGame();
	showScore();
	system("pause");
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值