C语言利用控制台实现贪吃蛇

1.游戏介绍

游戏预览

QQ录屏20240218160813

2.准备

运用结构体链表Win32 API等知识,我们尝试实现贪吃蛇小游戏

Win32 API

Windows 这个多作业系统除了协调应⽤程序的执⾏、分配内存、管理资源之外, 它同时也是⼀个很⼤ 的服务中⼼,调⽤这个服务中⼼的各种服务(每⼀种服务就是⼀个函数),可以帮应⽤程序达到开启 视窗、描绘图形、使⽤周边设备等⽬的,由于这些函数服务的对象是应⽤程序(Application), 所以便 称之为 Application Programming Interface,简称 API 函数。WIN32 API也就是Microsoft Windows 32位平台的应⽤程序编程接⼝。

总之可以使用Win32 API中的函数帮助游戏实现(如键盘的按下,光标的移动等)

游戏总概图:

3.打印、光标控制、键盘控制

3.1宽字符的打印

我们在实现贪吃蛇时用□、●、★分别表示墙体、蛇身、食物,然而他们所占的位置是普通英文字符的两倍,如图。

<locale.h>提供的函数⽤于控制C标准库中对于不同的地区会产⽣不⼀样⾏为的部分

其中setlocale 函数⽤于修改当前地区,以便于打印宽字符

	//修改配置本地中文环境
	setlocale(LC_ALL, "");

怎样打印宽字符呢?

在使用修改配置本地中文环境后使用wprintf(L"%c",L'●');即可打印

#include <stdio.h>
#include<locale.h>

int main()
{
	setlocale(LC_ALL, "");
	printf("%c%c\n", 'a', 'b');
	wprintf(L"%c\n", L'●');
	wprintf(L"%c\n", L'★');
	wprintf(L"%c\n", L'博');
	wprintf(L"%c\n", L'客');
	return 0;
}

 打印结果:

  

则我们可以定义宏来简化

#define WALL L'□' 
#define BODY L'●'
#define FOOD L'★' 

3.2光标控制

再包含了<windows.h>、<stdbool.h>后可以封装以下函数用于隐藏光标、控制光标位置,主要涉及到Win32 API中的函数的运用,不用太过仔细钻研。

//隐藏光标
void HideCursor()
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获得句柄位置
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(handle, &CursorInfo);//获取控制台光标信息
	CursorInfo.bVisible = false;
	SetConsoleCursorInfo(handle, &CursorInfo);
}


void SetPos(int x, int y)//设置光标位置
{
	//获得句柄位置
	HANDLE hanlde = GetStdHandle(STD_OUTPUT_HANDLE);

	//根据句柄设置光标位置
	COORD pos = { x,y };
	SetConsoleCursorPosition(hanlde, pos);

}

4.有关结构体、枚举体的声明

单独创建一个头文件放入这些头文件,及放入后面自定义函数的声明

通过GetAsyncKeyState()的返回值可以判断一个按键是否被按下

定义一个宏调用GetAsyncKeyState()以便控制蛇移动
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)

#pragma once
#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
#include <stdbool.h>
#include<locale.h>
#include<time.h>

#define WALL L'□' 
#define BODY L'●'
#define FOOD L'★' 

//蛇默认的起始坐标
#define POS_X 24
#define POS_Y 5
//定义一个宏调用GetAsyncKeyState()函数用于判断键盘按键是否被按下
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)

//游戏的状态
enum GAME_STATUS
{
	NORMAL=1,//正常游戏
	ESC,//按ESC退出
	KILL_BY_WALL,//撞墙而死
	KILL_BY_SELF//被自己杀死
};
//蛇的行走方向
enum DIRECTION
{
	UP=1,
	DOWN,
	LEFT,
	RIGHT
};


//节点结构体
typedef struct SnakeNode
{
	int x;
	int y;
	struct ShakeNode* next;
}SnakeNode,* pSnakeNode;

//游戏主体结构
typedef struct SnakeGame
{
	pSnakeNode psnake;//指向蛇身(蛇头)的指针
	pSnakeNode pfood;//指向食物的结构体的指针
	int score;//得分
	int FoodWeight;//当前一个零食价值的分数
	int SleepTime;//休眠时间即速度
	enum GAME_STATUS status;//当前游戏状态
	enum DIRECTION dir;//蛇的运动方向
}SnakeGame,* pSnakeGame;//pSnakeGame 等价于 SnakeGame*

5.GameStart函数

GameStart()函数这一阶段我们进行游戏开始的准备工作,先设置控制台大小为长100格,宽30格(可根据自己喜好自行调整,后面的打印光标的坐标等也可相应调整)

  WelcomeToGame()用于打印游戏开始前的帮助信息

void GameStart(pSnakeGame ps)//游戏的初始化
{
	system("mode con cols=100 lines=30");//设置控制台界面大小
	system("title 贪吃蛇");//设置控制台名称
	
	
	//打印欢迎信息
	WelcomeToGame();

	//绘制地图
	CreateMap();

	//初始化蛇
	InitSnake(ps);

	//创建食物
	CreateFood(ps);

}

//打印欢迎信息
void WelcomeToGame() {
	//欢迎
	SetPos(35,10);
	printf("欢迎来到贪吃蛇小游戏!\n");
	
	SetPos(35, 15);
	system("pause");//停止指令,等待按下键继续。system函数需包含<windows.h>
	system("cls");

	//功能介绍
	SetPos(15,10);
	printf("用↑,↓,←,→来控制蛇的移动,F3是加速,F4是减速,按ESC退出");
	SetPos(15, 11);
	printf("加速吃零食能得到更高分数。");
	SetPos(35, 15);
	system("pause");//停止指令,等待按下键继续。system函数需包含<windows.h>

	system("cls");//清除屏幕指令


}

-CreateMap()

我们创建地图背景长56格(28个宽字符)、宽27格

void CreateMap() {
	
	SetPos(0, 0);//移动光标
	//上
	for (int i = 0; i <= 56; i += 2)
	{
		wprintf(L"%lc",WALL);
	}
	 
	//下
	SetPos(0, 26);
	for (int i = 0; i <= 56; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	//左
	for (int i = 1; i <= 25; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}
	
	//右
	for (int i = 1; i <= 25; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc", WALL);
	}
}

-InitSnake()

初始化蛇结构体,以及游戏中其他数值如基础分值、休眠时间等

//初始化蛇
void InitSnake(pSnakeGame ps) {

	pSnakeNode pcur =  NULL;
	for (int i = 0; i < 5; i++)
	{
		pcur = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (pcur == NULL)
		{
			perror("InitSnake():malloc()");
			return;
		}
		pcur->x = POS_X + 2 * i;
		pcur->y = POS_Y;
		pcur->next = NULL;
		//头插法
		if (ps->psnake == NULL)
		{
			ps->psnake = pcur;
		}
		else
		{
			pcur->next = ps->psnake;
			ps->psnake = pcur;
		}
	}

	pcur = ps->psnake;
	while (pcur)
	{
		SetPos(pcur->x, pcur->y);
		wprintf(L"%lc", BODY);
		pcur = pcur->next;
	}
	//贪吃蛇的其他信息初始化
	ps->dir = RIGHT;
	ps->FoodWeight = 10;
	ps->pfood = NULL;
	ps->score = 0;
	ps->SleepTime = 200;
	ps->status = NORMAL;

}

-CreateFood()

srand((unsigned int)time(NULL));//用于设置随机数的种子,以便于后面x、y取随机值

//创建食物
void CreateFood(pSnakeGame ps) {
	int x = 0;
	int y = 0;
	srand((unsigned int)time(NULL));
again:
	do
	{
		x = rand() % 52 + 2;
		y = rand() % 24 + 1;
	} while (x % 2 != 0);//x必为偶数,以便于宽字符正常打印

	pSnakeNode pcur = ps->psnake;
	while (pcur)
	{
		if (x == pcur->x && y == pcur->y)//食物不能与蛇身重叠
			goto again;
		pcur = pcur->next;
	}

	pSnakeNode pfood = (pSnakeNode)malloc(sizeof(SnakeNode));
	

	pfood->x = x;
	pfood->y = y;
	ps->pfood = pfood;
	SetPos(x, y);
	wprintf(L"%c", FOOD);


}

6.GameRun函数

GameRun()主要实现

1.按下键盘的判断,然后2.蛇进行移动,然后3.判断蛇的状态,若ps->status == NORMAL 则1-3再次循环

void GameRun(pSnakeGame ps)//游戏的运行
{
	PrintHelpInfo();
do {
	//当前的分数情况
	SetPos(62, 10);
	printf("总分:%5d\n", ps->score);
	SetPos(62, 11);
	printf("食物的分值:%3d\n", ps->FoodWeight);
	
		if (KEY_PRESS(VK_UP) && ps->dir != DOWN)
		{
			ps->dir = UP;
		}
		else if (KEY_PRESS(VK_DOWN) && ps->dir != UP)
		{
			ps->dir = DOWN;
		}
		else if (KEY_PRESS(VK_LEFT) && ps->dir != RIGHT)
		{
			ps->dir = LEFT;
		}
		else if (KEY_PRESS(VK_RIGHT) && ps->dir != LEFT)
		{
			ps->dir = RIGHT;
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			ps->status = ESC;
			break;
		}
		else if (KEY_PRESS(VK_SPACE))
		{
			//游戏要暂停
			pause();//暂定和回复暂定
		}
		else if (KEY_PRESS(VK_F3))
		{
			if (ps->SleepTime >= 80)
			{
				ps->SleepTime -= 30;
				ps->FoodWeight += 2;

			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			if(ps->SleepTime <= 260)
			ps->SleepTime += 30;
			ps->FoodWeight -= 2;
		}
		//蛇走一步
    		SnakeMove(ps);

		//睡眠
		Sleep(ps->SleepTime);


	} while (ps->status == NORMAL);
}

-pause()

在判断按键是否按下过时若按下空格则会进入此函数,在此函数中1、打印提示信息 2、在相同位置打印等长空格 。以实现提示信息闪烁然后1-2循环直到再次按下空格跳出循环

//游戏暂停
void pause()
{
	SetPos(62, 5);
	printf("已暂停,按空格开始/继续");
	while (1)
	{

		if (KEY_PRESS(VK_SPACE))
		{
			break;
		}
		else
		{
			SetPos(62, 5);
			printf("已暂停,按空格开始/继续");
			Sleep(500);
			SetPos(62, 5);
			printf("                       ");
			Sleep(500);
		}
	}
	SetPos(62, 5);
	printf("                       ");

}

-SnakeMove()

1、创建一个临时节点指针 2、判断运动方向 3、根据节点指针找到蛇头运行的下一个位置并存到临时节点中 4、判断此位置是否是食物 5、判断是否撞墙或被自己杀死

//蛇走一步
void SnakeMove(pSnakeGame ps) {
	pSnakeNode pNext = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (pNext == NULL)
	{
		perror("SnakeMove()::malloc()");
		return;
	}

	switch (ps->dir)
	{
	case UP:
		pNext->x = ps->psnake->x;
		pNext->y = ps->psnake->y - 1;
		break;
	case DOWN:
		pNext->x = ps->psnake->x;
		pNext->y = ps->psnake->y + 1;
		break;
	case LEFT:
		pNext->x = ps->psnake->x-2;
		pNext->y = ps->psnake->y;
		break;
	case RIGHT:
		pNext->x = ps->psnake->x+2;
		pNext->y = ps->psnake->y;
		break;
	}
	if (NextIsFood(ps,pNext))
	{
		EatFood(ps,pNext);
	}
	else
	{
		NotEatFood(ps,pNext);
	}
	KillByWall(ps,pNext);

	KillBySelf(ps,pNext);
}

-NextIsFood()

//是则返回1,不是则返回0
int NextIsFood(pSnakeGame ps, pSnakeNode pNext)
{
	if (pNext->x == ps->pfood->x && pNext->y == ps->pfood->y)
		return 1;
	else
		return 0;

}

-EatFood()

如果吃到食物,先将下一个位置头插到蛇身的链表上,第二遍历链表并打印蛇身,其次分数加上相应分值,最后再创建一个食物

//吃到食物
void EatFood(pSnakeGame ps,pSnakeNode pNext)
{
	//头插一个蛇身节点
	pNext->next = ps->psnake;
	ps->psnake = pNext;

	//打印蛇
	pSnakeNode pcur = ps->psnake;
	while (pcur)
	{
		SetPos(pcur->x, pcur->y);
		wprintf(L"%lc", BODY);
		pcur = pcur->next;
	}
	ps->score += ps->FoodWeight;

	//释放旧的食物
	free(ps->pfood);
	//新建食物
	CreateFood(ps);
}

-NotEatFood()

若下一个位置不是食物,把这个节点头插入蛇身,然后遍历蛇身并打印,然后删除蛇身的最后一个节点

//没吃到食物
void NotEatFood(pSnakeGame ps, pSnakeNode pNext)
{


	//头插一个蛇身节点
	pNext->next = ps->psnake;
	ps->psnake = pNext;

	SnakeNode* pcur = ps->psnake;
	pSnakeNode next = pcur->next;
	while (next->next)
	{
		SetPos(pcur->x, pcur->y);
		wprintf(L"%lc", BODY);
		pcur = pcur->next;
		next = pcur->next;
	}
	
	//将之前的最后一个节点用“  ”覆盖
	SetPos(next->x,next->y);
	printf("  " );
	free(next);
	pcur->next = NULL;

}

-KillByWall()、KillBySelf()

判断是否撞墙即将下一个位置坐标和墙体作比较,判断是否撞上自己即遍历链表将每个节点和下一个位置坐标做比较看是否重合

若撞墙则将游戏状态改变 ps->status = KILL_BY_WALL;

若撞上自己则将游戏状态改变 ps->status = KILL_BY_SELF;

//被墙杀死
void KillByWall(pSnakeGame ps, pSnakeNode pNext) {
	if (pNext->x == 0 || pNext->x == 56 || pNext->y == 0 || pNext->y == 26)
		ps->status = KILL_BY_WALL;
}

//撞上自己杀死
void KillBySelf(pSnakeGame ps, pSnakeNode pNext) {
	pSnakeNode pcur = ps->psnake->next;
	while (pcur)
	{
		if (pNext->x == pcur->x && pNext->y == pcur->y)
		{
			ps->status = KILL_BY_SELF;
			return;
		}
		pcur = pcur->next;
	}
}

7.GameEnd函数

此函数用于实现游戏结束后提示信息的打印,然后遍历链表释放贪吃蛇的链表资源

void GameEnd(pSnakeGame ps)//游戏的结束
{
	SetPos(15, 12);
	switch (ps->status)
	{
	case ESC:
		printf("主动退出游戏,正常退出\n");
		break;
	case KILL_BY_WALL:
		printf("很遗憾,撞墙了,游戏结束\n");
		break;
	case KILL_BY_SELF:
		printf("很遗憾,咬到自己了,游戏结束\n");
		break;
	}

	//释放贪吃蛇的链表资源
	pSnakeNode pcur = ps->psnake;
	while (pcur)
	{
		pSnakeNode del = pcur;
		pcur = pcur->next;
		free(del);
	}
	free(ps->pfood);

}

8.游戏总流程game()

在    GameStart(&snake);GameRun(&snake);GameEnd(&snake);进行后打印提示信息若按下N退出整个循环,按下Y继续游戏

void game()
{
	
	HideCursor();//隐藏光标
	do
	{
		SnakeGame snake = { 0 };
		GameStart(&snake);
		GameRun(&snake);
		GameEnd(&snake);
		SetPos(15, 13);
		printf("想再玩一次吗(Y/N)");
		do {
			Sleep(100);
			if (KEY_PRESS('n') || KEY_PRESS('N'))
			{
				system("cls");
				return;
			}
			else if (KEY_PRESS('y') || KEY_PRESS('Y'))
				break;
		} while (1);


	} while (1);
}

-main()

int main()
{
	//修改配置本地中文环境
	setlocale(LC_ALL, "");
	
	game();

	return 0;
}

9.参考代码

Snake.h

#pragma once
#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
#include <stdbool.h>
#include<locale.h>
#include<time.h>

#define WALL L'□' 
#define BODY L'●'
#define FOOD L'★' 

//蛇默认的起始坐标
#define POS_X 24
#define POS_Y 5
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)

//游戏的状态
enum GAME_STATUS
{
	NORMAL=1,//正常游戏
	ESC,//按ESC退出
	KILL_BY_WALL,//撞墙而死
	KILL_BY_SELF//被自己杀死
};
//蛇的行走方向
enum DIRECTION
{
	UP=1,
	DOWN,
	LEFT,
	RIGHT
};


//节点结构体
typedef struct SnakeNode
{
	int x;
	int y;
	struct ShakeNode* next;
}SnakeNode,* pSnakeNode;

//游戏主体结构
typedef struct SnakeGame
{
	SnakeNode* psnake;//指向蛇身的指针
	SnakeNode* pfood;//指向食物的结构体的指针
	int score;//得分
	int FoodWeight;//当前一个零食价值的分数
	int SleepTime;//休眠时间即速度
	enum GAME_STATUS status;//当前游戏状态
	enum DIRECTION dir;//蛇的运动方向
}SnakeGame,* pSnakeGame;//pSnakeGame 等价于 SnakeGame*


void SetPos(int x, int y);//设置光标位置
void HideCursor();//隐藏光标

//游戏的初始化
void GameStart();
//打印欢迎信息
void WelcomeToGame();
//绘制地图
void CreateMap();
//初始化蛇
void InitSnake(ps);
//创建食物
void CreateFood();

//游戏的运行
void GameRun();

//打印帮助信息
void PrintHelpInfo();

//游戏暂停
void pause();
//蛇走一步
void SnakeMove(pSnakeGame ps);
//吃到食物
void EatFood(pSnakeGame ps, pSnakeNode pNext);
//没吃到食物
void NotEatFood(pSnakeGame ps, pSnakeNode pNext);
//被墙杀死
void KillByWall(pSnakeGame ps,pSnakeNode pNext);
//撞上自己杀死
void KillBySelf(pSnakeGame ps,pSnakeNode pNext);

//游戏的结束
void GameEnd();

Snake.c

#include"Snake.h"

void SetPos(int x, int y)//设置光标位置
{
	//获得句柄位置
	HANDLE hanlde = GetStdHandle(STD_OUTPUT_HANDLE);

	//根据句柄设置光标位置
	COORD pos = { x,y };
	SetConsoleCursorPosition(hanlde, pos);

}


//隐藏光标
void HideCursor()
{
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获得句柄位置
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(handle, &CursorInfo);//获取控制台光标信息
	CursorInfo.bVisible = false;
	SetConsoleCursorInfo(handle, &CursorInfo);
}


//打印欢迎信息
void WelcomeToGame() {
	//欢迎
	SetPos(35,10);
	printf("欢迎来到贪吃蛇小游戏!\n");
	
	SetPos(35, 15);
	system("pause");//停止指令,等待按下键继续system函数需包含<windows.h>
	system("cls");

	//功能介绍
	SetPos(15,10);
	printf("用↑,↓,←,→来控制蛇的移动,F3是加速,F4是减速,按ESC退出");
	SetPos(15, 11);
	printf("加速吃零食能得到更高分数。");
	SetPos(35, 15);
	system("pause");//停止指令,等待按下键继续

	system("cls");


}



//绘制地图
void CreateMap() {
	
	SetPos(0, 0);//移动光标
	//上
	for (int i = 0; i <= 56; i += 2)
	{
		wprintf(L"%lc",WALL);
	}
	 
	//下
	SetPos(0, 26);
	for (int i = 0; i <= 56; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	//左
	for (int i = 1; i <= 25; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}
	
	//右
	for (int i = 1; i <= 25; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc", WALL);
	}
}

//初始化蛇
void InitSnake(pSnakeGame ps) {

	pSnakeNode pcur =  NULL;
	for (int i = 0; i < 5; i++)
	{
		pcur = (pSnakeNode)malloc(sizeof(SnakeNode));
		if (pcur == NULL)
		{
			perror("InitSnake():malloc()");
			return;
		}
		pcur->x = POS_X + 2 * i;
		pcur->y = POS_Y;
		pcur->next = NULL;
		//头插法
		if (ps->psnake == NULL)
		{
			ps->psnake = pcur;
		}
		else
		{
			pcur->next = ps->psnake;
			ps->psnake = pcur;
		}
	}

	pcur = ps->psnake;
	while (pcur)
	{
		SetPos(pcur->x, pcur->y);
		wprintf(L"%lc", BODY);
		pcur = pcur->next;
	}
	//贪吃蛇的其他信息初始化
	ps->dir = RIGHT;
	ps->FoodWeight = 10;
	ps->pfood = NULL;
	ps->score = 0;
	ps->SleepTime = 200;
	ps->status = NORMAL;

}

//创建食物
void CreateFood(pSnakeGame ps) {
	int x = 0;
	int y = 0;
	srand((unsigned int)time(NULL));
again:
	do
	{
		x = rand() % 52 + 2;
		y = rand() % 24 + 1;
	} while (x % 2 != 0);

	pSnakeNode pcur = ps->psnake;
	while (pcur)
	{
		if (x == pcur->x && y == pcur->y)
			goto again;
		pcur = pcur->next;
	}

	pSnakeNode pfood = (pSnakeNode)malloc(sizeof(SnakeNode));
	

	pfood->x = x;
	pfood->y = y;
	ps->pfood = pfood;
	SetPos(x, y);
	wprintf(L"%c", FOOD);


}

void GameStart(pSnakeGame ps)//游戏的初始化
{
	system("mode con cols=100 lines=30");//设置控制台界面大小
	system("title 贪吃蛇");
	
	
	//打印欢迎信息
	WelcomeToGame();

	//打印帮助信息
	PrintHelpInfo();

	绘制地图
	CreateMap();


	//初始化蛇
	InitSnake(ps);

	//创建食物
	CreateFood(ps);


}

void PrintHelpInfo()
{
	SetPos(62, 15);
	printf("1.不能穿墙,不能咬到自己");
	SetPos(62, 16);
	printf("2.用 ↑.↓.←.→ 来控制蛇的移动");
	SetPos(62, 17);
	printf("3.F3是加速,F4是减速");
	SetPos(62, 18);
	printf("加速食物的分值会变高");
	SetPos(62, 19);
	printf("减速食物的分值则会变低");


}

void GameRun(pSnakeGame ps)//游戏的运行
{
	PrintHelpInfo();
do {
	//当前的分数情况
	SetPos(62, 10);
	printf("总分:%5d\n", ps->score);
	SetPos(62, 11);
	printf("食物的分值:%3d\n", ps->FoodWeight);
	
		if (KEY_PRESS(VK_UP) && ps->dir != DOWN)
		{
			ps->dir = UP;
		}
		else if (KEY_PRESS(VK_DOWN) && ps->dir != UP)
		{
			ps->dir = DOWN;
		}
		else if (KEY_PRESS(VK_LEFT) && ps->dir != RIGHT)
		{
			ps->dir = LEFT;
		}
		else if (KEY_PRESS(VK_RIGHT) && ps->dir != LEFT)
		{
			ps->dir = RIGHT;
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			ps->status = ESC;
			break;
		}
		else if (KEY_PRESS(VK_SPACE))
		{
			//游戏要暂停
			pause();//暂定和回复暂定
		}
		else if (KEY_PRESS(VK_F3))
		{
			if (ps->SleepTime >= 80)
			{
				ps->SleepTime -= 30;
				ps->FoodWeight += 2;

			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			if(ps->SleepTime <= 260)
			ps->SleepTime += 30;
			ps->FoodWeight -= 2;
		}
		//蛇走一步
    		SnakeMove(ps);

		//睡眠
		Sleep(ps->SleepTime);


	} while (ps->status == NORMAL);
}

//游戏暂停
void pause()
{
	SetPos(62, 5);
	printf("已暂停,按空格开始/继续");
	while (1)
	{

		if (KEY_PRESS(VK_SPACE))
		{
			break;
		}
		else
		{
			SetPos(62, 5);
			printf("已暂停,按空格开始/继续");
			Sleep(500);
			SetPos(62, 5);
			printf("                       ");
			Sleep(500);
		}
	}
	SetPos(62, 5);
	printf("                       ");

}

//蛇走一步
void SnakeMove(pSnakeGame ps) {
	pSnakeNode pNext = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (pNext == NULL)
	{
		perror("SnakeMove()::malloc()");
		return;
	}

	switch (ps->dir)
	{
	case UP:
		pNext->x = ps->psnake->x;
		pNext->y = ps->psnake->y - 1;
		break;
	case DOWN:
		pNext->x = ps->psnake->x;
		pNext->y = ps->psnake->y + 1;
		break;
	case LEFT:
		pNext->x = ps->psnake->x-2;
		pNext->y = ps->psnake->y;
		break;
	case RIGHT:
		pNext->x = ps->psnake->x+2;
		pNext->y = ps->psnake->y;
		break;
	}
	if (NextIsFood(ps,pNext))
	{
		EatFood(ps,pNext);
	}
	else
	{
		NotEatFood(ps,pNext);
	}
	KillByWall(ps,pNext);

	KillBySelf(ps,pNext);
}

//是则返回1,不是则返回0
int NextIsFood(pSnakeGame ps, pSnakeNode pNext)
{
	if (pNext->x == ps->pfood->x && pNext->y == ps->pfood->y)
		return 1;
	else
		return 0;

}

//吃到食物
void EatFood(pSnakeGame ps,pSnakeNode pNext)
{
	//头插一个蛇身节点
	pNext->next = ps->psnake;
	ps->psnake = pNext;

	//打印蛇
	pSnakeNode pcur = ps->psnake;
	while (pcur)
	{
		SetPos(pcur->x, pcur->y);
		wprintf(L"%lc", BODY);
		pcur = pcur->next;
	}
	ps->score += ps->FoodWeight;

	//释放旧的食物
	free(ps->pfood);
	//新建食物
	CreateFood(ps);
}

//没吃到食物
void NotEatFood(pSnakeGame ps, pSnakeNode pNext)
{


	//头插一个蛇身节点
	pNext->next = ps->psnake;
	ps->psnake = pNext;

	SnakeNode* pcur = ps->psnake;
	pSnakeNode next = pcur->next;
	while (next->next)
	{
		SetPos(pcur->x, pcur->y);
		wprintf(L"%lc", BODY);
		pcur = pcur->next;
		next = pcur->next;
	}
	
	//将之前的最后一个节点用“  ”覆盖
	SetPos(next->x,next->y);
	printf("  " );
	free(next);
	pcur->next = NULL;

}

//被墙杀死
void KillByWall(pSnakeGame ps, pSnakeNode pNext) {
	if (pNext->x == 0 || pNext->x == 56 || pNext->y == 0 || pNext->y == 26)
		ps->status = KILL_BY_WALL;
}
//撞上自己杀死
void KillBySelf(pSnakeGame ps, pSnakeNode pNext) {
	pSnakeNode pcur = ps->psnake->next;
	while (pcur)
	{
		if (pNext->x == pcur->x && pNext->y == pcur->y)
		{
			ps->status = KILL_BY_SELF;
			return;
		}
		pcur = pcur->next;
	}
}


void GameEnd(pSnakeGame ps)//游戏的结束
{
	SetPos(15, 12);
	switch (ps->status)
	{
	case ESC:
		printf("主动退出游戏,正常退出\n");
		break;
	case KILL_BY_WALL:
		printf("很遗憾,撞墙了,游戏结束\n");
		break;
	case KILL_BY_SELF:
		printf("很遗憾,咬到自己了,游戏结束\n");
		break;
	}

	//释放贪吃蛇的链表资源
	pSnakeNode pcur = ps->psnake;
	while (pcur)
	{
		pSnakeNode del = pcur;
		pcur = pcur->next;
		free(del);
	}
	free(ps->pfood);

}

test.c

#include"Snake.h"


void game()
{
	
	HideCursor();//隐藏光标
	do
	{
		SnakeGame snake = { 0 };
		GameStart(&snake);
		GameRun(&snake);
		GameEnd(&snake);
		SetPos(15, 13);
		printf("想再玩一次吗(Y/N)");
		do {
			Sleep(100);
			if (KEY_PRESS('n') || KEY_PRESS('N'))
			{
				system("cls");
				return;
			}
			else if (KEY_PRESS('y') || KEY_PRESS('Y'))
				break;
		} while (1);


	} while (1);
}


int main()
{
	//修改配置本地中文环境
	setlocale(LC_ALL, "");
	
	game();

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值