贪吃蛇--C语言

欢迎来到本期频道!

在学习C语言的道路上,有些小伙伴可能还没遇到自己喜欢的游戏- -

希望贪吃蛇能够给你带来乐趣- -

一:思考代码实现方向

1.我们要用C语言实现该游戏,就需要明白蛇是维护的对象,因为玩的就是蛇;由于C语言是一门面向过程的编程语言,所以我们主要实现的是对象的运行过程,既蛇的运行过程–例如蛇身的变化,蛇的状态,蛇的方向等。

2.由此可以知道,我们需要创建结构体蛇,为了方便维护蛇,该结构体应该包含相关蛇的基本属性–例如蛇身,蛇的方向,食物,蛇的状态等;然后根据蛇的属性来实现蛇的运行过程。

二:梳理逻辑

在这里插入图片描述

1.写大框架

可以把游戏分为三个阶段,准备阶段-进行阶段-结束阶段。其实这就是大框架。我们在编写代码时,可以先把大框架封装成函数,这样不仅思路清晰,还能梳理逻辑。例如:

// test.c
void game()
{
	//由于这里对象是蛇,创建一个蛇
	Snake  snake={0};
	//三个框架都会涉及蛇的属性,所以传入地址
	//准备游戏
	game_start(&snake);
	//进行游戏
	game_run(&snake);
	//结束游戏
	game_end(&snake);
}
然后在头文件中声明并将所需类型创建:
#pragma once
typedef struct Snakenode	//创建蛇节点类型
{
	int x;		//节点位置(x,y)
	int y;
	wchar_t ch;		//蛇身节点打印的宽字符
	struct Snakenode* next;
}Snakenode,* Psnakenode; 

typedef enum Dir
{
	Up,
	Down,
	Left,
	Right
}Dir;
typedef	enum Speed
{
	One=1,
	Two,
	Three,
	Four,
	Five
}Speed;
typedef enum State
{
	Ok,
	Kill_Byself,
	Kill_Bywall,
	End_Normal
}
typedef struct Snake		//创建蛇类型
{
	Psnakenode phead;	//蛇身的头结点,因为蛇身是链表所以我们需要创建链表节点类型
	//蛇的方向上下左右,我们可以用枚举变量来存储
	Dir dir;
	//同样蛇的速度等级、状态
	Speed speed;
	Stata stata;
	//食物--和蛇节点一样的类型
	Psnakenode food;
	//走一步的时间
	int sleeptime;
	//食物的分数
	int food_weight;
	//总分
	int grade;
}Snake,*Psnake;

//准备游戏声明
void game_start(Psnake sp);
//进行游戏声明
void game_run(Psnake sp);
//结束游戏声明
void game_end(Psnake sp);

2.写子框架

有了三个大框架,就可以准备子框架了,例如:

//snake.c
void game_start(Psnake sp)	//准备阶段
{
	//美化控制台 
	welcome(sp);//欢迎界面
	print_wall(sp);//绘制地图--墙
	//提示信息
	gradeinfo(sp);//分数打印
}
void game_run(Psnake sp)
{
	//初始化蛇
	snake_init(sp);
	//这里需要思考蛇是怎么一直走的,很明显是循环
	do
	{
		//获取用户按键信息
		
		//上下左右
		//space  esc
		//shift ctrl
		
		//根据效果(蛇的属性)来调整蛇
		snake_modify(sp);//调整蛇身
		snake_state(sp);//判断状态
		gradeinfo(sp);//打印分数
		//走一步的时间
	}while();//什么时候停下来呢?也就是看蛇的状态是否Ok-----循环一次蛇走一步,一直循环一直走
}
void game_end(Psnake sp)
{
	//销毁贪吃蛇的蛇身--动态申请的要归还
	snakebody_omit(sp);
	//打印结束提示信息
}

这里的函数声明可以放在头文件中。
有了这样的框架,是不是发现只剩下函数的实现了,但是好像又写不出来🤔。
因为贯穿整个游戏的基本方法还不知道 :

❶.怎么打印宽字符
❷.怎么隐藏光标
❸.怎么指定位置打印
❹.怎么获取按键信息

Ⅰ怎么打印宽字符

要打印宽字符,需要本地化,例如:

setlocale

#include<locale.h>	//本地化的头文件
setlocale((LC_ALL,"");	//本地化
wprintf(L"%lc",L'●');	//打印宽字符的函数
这里需要知道,平时所打印的字符'a' . '1 '是窄字符,而控制台的坐标是以窄字符为单位的,所以宽字符通常占了两个窄字符:

在这里插入图片描述

Ⅱ 怎么隐藏光标

指定位置打印也就是将光标位置设定到指定位置。
第一步:获取输出设备的句柄,既屏幕的句柄。句柄就是指针,类型为HANDLE,也可以理解成锅铲的手柄。

GetStdHandle

#include<windows.h>
HANDLE poutput=GetStdHandle(STD_OUTPUT_HANDLE);
第二步:获取光标信息

CONSOLE_CURSOR_INFO
GetConsoleCursorInfo

CONSOLE_CURSOR_INFO cursor;
GetConsoleCursorInfo(poutput,&cursor);
第三步:设定光标信息

SetConsoleCursorInfo

cursor.bVisible = false;	//光标属性设定为不可见
SetConsoleCursorInfo(poutput,&cursor);

Ⅲ 怎么指定位置打印

第一步:获取输出设备的句柄.
HANDLE poutput=GetStdHandle(STD_OUTPUT_HANDLE);
第二步:设置指定位置

SetConsoleCursorPosition

COORD pos={0,0};	//COORD类型可以存储 控制台光标的坐标。
SetConsoleCursorPosition(poutput,pos);	//把光标位置放到了(0,0)位置。

Ⅳ 怎么获取按键信息

GetAsyncKeyState

 #define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )		//检测按键是否被按过
VK是每个按键所对应的虚拟值。

3.从蛇身角度梳理游戏逻辑

YES
NO
YES
NO
墙内打印出蛇
方向
确定蛇头位置并打印蛇头
食物
销毁食物并重新产生
销毁蛇尾节点并用空格覆盖
蛇头是墙或者自身节点
游戏结束
以上代码纯属举例,与下方代码可能不一致。

三:实现

有了以上的框架与逻辑,相信聪明的小伙伴已经迫不及待了,赶紧动起手吧,VS启动。
以下是我写的代码,有误请告知。

Test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Snake.h"
void game()
{

	srand((unsigned int)time(NULL));
	char ch = 0;
	do
	{
		system("cls");
		Snake snake = { 0 };
		Psnake sp = &snake;
		//准备游戏阶段
		Game_Start(sp);
		//游戏运行阶段
		Game_Run(sp);
		//游戏结束阶段
		Game_End(sp);
		Setpos(110, 23);
		printf("是否再来一把?");
		Setpos(110, 25);
		printf("(Y/N):");
		while (isspace(ch = getchar()));
		while (getchar() != '\n');
	} while (ch == 'Y' || ch == 'y');
	Setpos(0, 46);
}
int main()
{
	game();
	return 0;
}

Snake.h

#pragma once
#include<stdio.h>
#include<Windows.h>
#include<locale.h>
#include<stdbool.h>
#include<time.h>
#include"vld.h"

//这是个人喜好,换颜色用的
#define WHITE FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE
#define YELLOW	FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN
#define RED	FOREGROUND_INTENSITY|FOREGROUND_RED
#define PURPLE FOREGROUND_INTENSITY|FOREGROUND_RED|FOREGROUND_BLUE
#define BLUE	FOREGROUND_INTENSITY|FOREGROUND_BLUE
#define CYAN	FOREGROUND_INTENSITY|FOREGROUND_GREEN|FOREGROUND_BLUE
#define GREEN	FOREGROUND_INTENSITY|FOREGROUND_GREEN

#define SET_COLOR(x)	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), x);

#define SnakeInitLen 5
#define SnodeInitPos_x 12
#define SnodeInitPos_y 10
#define	WALL L'◎'
#define BODY L'◆'
#define FOOD L'★'
#define HEAD L'●'

#define INIT_MID_TIME	200


typedef enum State
{
	Ok = 1,
	Kill_Byself,
	Kill_Wall,
	End_Normal
}State;
typedef enum Dir
{
	Up,
	Down,
	Left,
	Right
}Dir;
typedef enum Speed
{
	One = 1,
	Two,
	Three,
	Four,
	Five
}Speed;

typedef struct SnakeNode
{
	wchar_t ch;
	int x;
	int y;
	struct SnakeNode* next;
}SnakeNode, * Psnakenode;


//创建蛇--维护蛇的运行
typedef struct Snake
{
	Psnakenode phead;	//蛇本身
	Dir dir;	//蛇的方向
	Speed speed;	//蛇的速度
	Psnakenode food;	//蛇吃的食物
	int sleep_time;		//蛇走一步的时间
	int food_weight;	//单个食物分数
	int grade;			//蛇吃完食物后的总分
	State state;		//蛇的状态
}Snake, * Psnake;


//检测按键是否按过
#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )
//设置光标位置
void Setpos(int x, int y);


//游戏流程逻辑
void game();




//准备游戏阶段
void Game_Start(Psnake sp);
//欢迎界面
void Welcom();
//打印墙体
void Wall();
//蛇的初始化
void Snake_Init(Psnake sp);
//创建蛇身
void Snake_Body(Psnake sp);
//打印蛇身
void Snake_Print(Psnake sp);
//创建食物
void Creat_Food(Psnake sp);
//分数信息打印
void grade_Info(Psnake sp);






//游戏运行阶段
void Game_Run(Psnake sp);
//调整蛇身
void Snake_New(Psnake sp);
//判断状态
void Snake_State(Psnake sp);


//游戏结束阶段
void Game_End(Psnake sp);

Snake.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Snake.h"
//设置光标位置
void Setpos(int x, int y)
{
	HANDLE poutput = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos = { x,y };
	SetConsoleCursorPosition(poutput, pos);
}
//欢迎界面
void Welcom()
{
	//设置光标位置
	Setpos(63, 22);
	printf("欢迎来到贪吃蛇小游戏!");
	Setpos(64, 48);
	system("pause");
	system("cls");
	Setpos(55, 21);
	printf("↑,→,↓,←分别代表蛇移动的方向");
	Setpos(55, 23);
	printf("Space,Esc分别代表暂停/继续和退出");
	Setpos(55, 25);
	printf("Shift,Ctrl分别代表加速和减速");
	Setpos(60, 48);
	system("pause");
	system("cls");
}
//打印墙体
void Wall()
{
	wchar_t ch = WALL;
	//上
	int i = 0;
	for (i = 0; i < 45; i++)
	{
		wprintf(L"%lc", ch);
	}
	//下
	Setpos(0, 44);
	for (i = 0; i < 45; i++)
	{
		wprintf(L"%lc", ch);
	}
	//左
	Setpos(0, 1);
	for (i = 0; i < 45 - 2; i++)
	{
		wprintf(L"%lc\n", ch);
	}
	//右

	for (i = 0; i < 45 - 2; i++)
	{
		Setpos((45 - 1) * 2, i + 1);
		wprintf(L"%lc", ch);
	}
}
//创建蛇身
void Snake_Body(Psnake sp)
{
	int i = 0;
	Psnakenode phead = NULL;
	Psnakenode cur = NULL;
	while (i < SnakeInitLen)
	{
		cur = (Psnakenode)malloc(sizeof(SnakeNode));
		if (cur == NULL)
		{
			printf("Snake_Body malloc fail");
			return;
		}
		if (phead == NULL)
		{
			phead = cur;
			phead->x = SnodeInitPos_x;
			phead->y = SnodeInitPos_y;
			phead->next = NULL;
		}
		else
		{
			cur->x = phead->x + 2;
			cur->y = phead->y;
			cur->next = phead;
			phead = cur;
		}
		cur->ch=BODY;
		i++;
	}
	sp->phead = phead;
	phead->ch = HEAD;
	phead = cur = NULL;
}
//打印蛇身
void Snake_Print(Psnake sp)
{
	Psnakenode cur = sp->phead->next;
	SET_COLOR(CYAN);
	Setpos(sp->phead->x, sp->phead->y);
	wprintf(L"%lc", sp->phead->ch);
	SET_COLOR(YELLOW);
	while (cur)
	{
		Setpos(cur->x, cur->y);
		wprintf(L"%lc", cur->ch);
		cur = cur->next;
	}
}
//创建食物
void Creat_Food(Psnake sp)
{
	int x = 0;
	int y = 0;
again:
	do
	{
		x = rand() % 85 + 2;//2-86
		y = rand() % 43 + 1;//1-43
	} while (x % 2 != 0);
	Psnakenode cur = sp->phead;
	while (cur)
	{
		if (cur->x == x && cur->y == y)
			goto again;
		cur = cur->next;
	}
	Psnakenode food = (Psnakenode)malloc(sizeof(SnakeNode));
	if (food == NULL)
	{
		perror("CreateFood  malloc  fail");
		return;
	}
	food->x = x;
	food->y = y;
	food->next = NULL;
	sp->food = food;
	food->ch = FOOD;
	Setpos(x, y);
	wprintf(L"\033[31;5m%lc\033[0m\r", food->ch);
}
//蛇的初始化
void Snake_Init(Psnake sp)
{
	//创建蛇身
	Snake_Body(sp);
	sp->dir = Right;
	Creat_Food(sp);
	sp->food_weight = 10;
	sp->grade = 0;
	sp->speed = One;
	sp->state = Ok;
	sp->sleep_time = INIT_MID_TIME;
}
//帮助信息打印
void grade_Info(Psnake sp)
{
	Setpos(110, 10);
	printf("速度:");
	SET_COLOR(GREEN);
	printf("%d级", sp->speed);
	SET_COLOR(WHITE);

	Setpos(110, 12);
	printf("食物分数:");
	SET_COLOR(GREEN);
	printf("%-2d", sp->food_weight);
	SET_COLOR(WHITE);

	Setpos(110, 14);
	printf("总分:");
	SET_COLOR(GREEN);
	printf("%-3d", sp->grade);
	SET_COLOR(WHITE);
}
//准备游戏阶段
void Game_Start(Psnake sp)
{
	//打印需要宽字符
	setlocale(LC_ALL, "");
	//美化控制台窗口以及隐藏光标
	system("mode con cols=150 lines=50");
	system("title 贪吃蛇");
	HANDLE poutput = GetStdHandle(STD_OUTPUT_HANDLE);//拿到输出控制台的句柄

	CONSOLE_CURSOR_INFO cursorinfo;		//存储控制台光标信息
	GetConsoleCursorInfo(poutput, &cursorinfo);//获取默认信息
	cursorinfo.bVisible = false;		//把不可见属性存储
	SetConsoleCursorInfo(poutput, &cursorinfo);	//设置成不可见

	//欢迎界面
	Welcom();
	//打印墙体
	Wall();
	//蛇的初始化
	Snake_Init(sp);
	//固定信息打印
	Setpos(110, 29);
	printf("↑,→,↓,←分别代表蛇移动的方向");
	Setpos(110, 34);
	printf("Space,Esc分别代表暂停/继续和退出");
	Setpos(110, 39);
	printf("Shift,Ctrl分别代表加速和减速");
	Setpos(140, 47);
	printf("@.M_XY");
	//蛇身打印
	Snake_Print(sp);
	//分数信息打印
	grade_Info(sp);
}
//判断下一个节点位置是否是食物
int IF_IsFood(Psnake sp, Psnakenode new)
{
	return (sp->food->x == new->x) && (sp->food->y == new->y);
}
//调整蛇身
void Snake_New(Psnake sp)
{
	Psnakenode new = (Psnakenode)malloc(sizeof(SnakeNode));
	if (sp->dir == Up)
	{
		new->x = sp->phead->x;
		new->y = sp->phead->y - 1;
	}
	else if (sp->dir == Down)
	{
		new->x = sp->phead->x;
		new->y = sp->phead->y + 1;
	}
	else if (sp->dir == Left)
	{
		new->x = sp->phead->x - 2;
		new->y = sp->phead->y;
	}
	else if (sp->dir == Right)
	{
		new->x = sp->phead->x + 2;
		new->y = sp->phead->y;
	}
	//两种情况的下一个头节点打印是一样的
	SET_COLOR(YELLOW);

	new->ch = sp->phead->ch;
	sp->phead->ch = sp->phead->next->ch;
	Setpos(sp->phead->x, sp->phead->y);
	printf("  ");
	Setpos(sp->phead->x, sp->phead->y);
	wprintf(L"%lc", sp->phead->ch);
	SET_COLOR(CYAN);
	Setpos(new->x, new->y);
	wprintf(L"%lc", new->ch);

	SET_COLOR(WHITE);

	//是食物
	printf("\a\a\a");
	int ret = IF_IsFood(sp, new);
	if (ret)
	{
		//加分
		sp->grade += sp->food_weight;
		//连表
		new->next = sp->phead;
		sp->phead = new;
		free(sp->food);
		Creat_Food(sp);
		new = NULL;
		return;
	}

	//不是食物
	Psnakenode cur = sp->phead;
	while (cur->next->next)
	{
		cur = cur->next;
	}
	Setpos(cur->next->x, cur->next->y);
	printf("  ");	//覆盖之前蛇身的打印
	free(cur->next);
	cur->next = NULL;
	new->next = sp->phead;
	sp->phead = new;
	new = cur = NULL;
}
//判断状态
void Snake_State(Psnake sp)
{
	//咬到自己
	Psnakenode cur = sp->phead->next;
	while (cur)
	{
		if (sp->phead->x == cur->x && sp->phead->y == cur->y)
		{
			sp->state = Kill_Byself;
			cur = NULL;
			return;
		}
		cur = cur->next;
	}
	//撞墙
	if (sp->phead->x == 0 ||
		sp->phead->x == (45 - 1) * 2 ||
		sp->phead->y == 0 ||
		sp->phead->y == (45 - 1))
	{
		sp->state = Kill_Wall;
		return;
	}
}
//游戏运行阶段--蛇的运行
void Game_Run(Psnake sp)
{

	do
	{
		//根据按键调整属性
		//上下左右
		if (KEY_PRESS(VK_UP) && sp->dir != Down)
		{
			sp->dir = Up;
		}
		else if (KEY_PRESS(VK_DOWN) && sp->dir != Up)
		{
			sp->dir = Down;
		}
		else if (KEY_PRESS(VK_LEFT) && sp->dir != Right)
		{
			sp->dir = Left;
		}
		else if (KEY_PRESS(VK_RIGHT) && sp->dir != Left)
		{
			sp->dir = Right;
		}
		//esc和space
		else if (KEY_PRESS(VK_SPACE))
		{
			Setpos(110, 25);
			printf("游戏已暂停");
			while (1)
			{
				if (KEY_PRESS(VK_SPACE))
					break;
				Sleep(100);
			}
			Setpos(110, 25);
			printf("           ");
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			sp->state = End_Normal;
		}
		//Shift和Ctrl
		else if (KEY_PRESS(VK_RSHIFT))
		{
			if (sp->speed < Five)
			{
				sp->speed += 1;
				sp->food_weight += 3;
				sp->sleep_time -= 30;
			}

		}
		else if (KEY_PRESS(VK_RCONTROL))
		{
			if (sp->speed > One)
			{
				sp->speed -= 1;
				sp->food_weight -= 3;
				sp->sleep_time += 30;
			}
		}
		//调整蛇身
		Snake_New(sp);
		//判断状态
		Snake_State(sp);
		//刷新信息
		grade_Info(sp);
		Sleep(sp->sleep_time);	//走一步的时间
	} while (sp->state == Ok);
}


//游戏结束阶段
void Game_End(Psnake sp)
{
	//销毁蛇
	Psnakenode cur = sp->phead;
	while (cur)
	{
		Psnakenode t = cur;
		cur = cur->next;
		free(t);
	}
	//销毁食物
	free(sp->food);
	sp->phead = sp->food = NULL;
	//根据状态打印提示信息
	SET_COLOR(BLUE);
	Setpos(110, 18);
	if (sp->state == Kill_Wall)
	{
		printf("HeHe你撞墙了!");
	}
	else if (sp->state == Kill_Byself)
	{
		printf("HiHi你咬到自己了!");
	}
	else if (sp->state == End_Normal)
	{
		printf("退出游戏中......\n");
	}
	Sleep(1000);
	SET_COLOR(WHITE);
}
本期分享就到这里了,希望能让你更热爱C语言。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值