贪吃蛇(c实现)

目录

游戏说明:

 第一个是又是封面,第二个为提示信息,第三个是游戏运行界面

游戏效果展示:

 游戏代码展示:

snack.c

 test.c

 snack.h

控制台程序的准备:

控制台程序名字修改:

 参考:mode命令(mode | Microsoft Learn)

游戏框架构建:

控制台屏幕上的坐标COORD:

隐藏光标:

 光标跳转

打印颜色设置:

初始化界面:

需要注意的地方就是:

 例如第一次的坐标就为 (i,j) 那么下一次坐标就为(i+2,j);

 宽字符打印准备:

初始化蛇与蛇的打印:

随机创建食物:

蛇的单向移动:

大致小部分已经实现完成,那么就利用游戏逻辑来实现剩余的代码;

添加方向的改变与判断蛇的各个状态判断:

 对该函数里面各个小函数进行代码展示:

最后,还有对应的就是运行是代码的逻辑展示

速度的控制(单位毫秒):

 

 最后的游戏收尾就是提示信息的打印:

最后一步便是锦上添花了,就是打印提示信息:

到这里就已经完成了,一共有三个页面:

 第一个是又是封面,第二个为提示信息,第三个是游戏运行界面


 

游戏说明:

  1. 按方向键上下左右,可以实现蛇移动方向的改变
  2. 按F3加速,F4减速
  3. 按ESC正常退出游戏,按空格暂停游戏
  4. 加速可以获得更多的分数
  5. 获得100即可获得胜利

 (待优化部分:背景音乐,记录历史最高得分)

 第一个是又是封面,第二个为提示信息,第三个是游戏运行界面

游戏效果展示:

贪吃蛇游戏当中蛇的移动速度可以进行调整,动图当中把速度调得较慢(速度太快导致动图上蛇身显示不全),下面给出的代码当中将蛇的速度调整到了合适的位置,大家可以试试。

贪吃蛇

 

 游戏代码展示:

snack.c

#define  _CRT_SECURE_NO_WARNINGS

#include"snack.h"
void color(int c)
{
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //颜色设置
	//注:SetConsoleTextAttribute是一个API(应用程序编程接口)
}
void cursor_hide()
{
	HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO CursorInfo;
	GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
	CursorInfo.bVisible = false; //隐藏控制台光标 
	SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态 
}

void SetPos(int x,int y)
{
	//获得标准输出设备的句柄
	HANDLE hOutput = NULL;
	hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

	//定位光标的位置,到pos
	COORD pos = { x,y };
	SetConsoleCursorPosition(hOutput, pos);
}

void Welcome_game()
{
	color(9);
	SetPos(35, 12);
	wprintf(L"欢迎来到贪吃蛇小游戏\n");
	SetPos(36, 18);
	system("pause");
	system("cls");
	SetPos(25, 14);
	wprintf(L"用 ↑.↓.←.→ 来控制蛇的移动");
	SetPos(25, 15);
	wprintf(L"按F3加速,F4减速");
	SetPos(25, 16);
	wprintf(L"按ESC正常退出游戏,按空格暂停游戏");
	SetPos(25, 17);
	wprintf(L"加速可以获得更多的分数");
	SetPos(25, 18);
	wprintf(L"由能力有限公司提供技术支持");
	SetPos(0, 25);
	system("pause");
	system("cls");
	color(7);
}

void CreatMap()
{
	color(12);
	//上
	for (int i = 0; i < 29; i++)
	{
		wprintf(L"%lc", L'□');
	}
	//下
	SetPos(0, 26);
	for (int i = 0; i < 29; i++)
	{
		wprintf(L"%lc", L'□');
	} 
	//左
	for (int i = 1; i <= 25; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", L'□');
	}
	//右
	for (int i = 1; i <= 25; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc", L'□');
	}
	color(7);
}



void CreateSnack(pSnack ps)
{
	color(10);
	//默认开始初始化为5个结点
	pSnackNode cur = NULL;
	for (int i = 0; i < 5; i++)
	{
		cur =(pSnackNode)malloc(sizeof(SnackNode));
		cur->next = NULL;
		cur->x = POS_X + i * 2;
		cur->y = POS_Y;
		if (ps->_psnack == NULL)//第一次头插
		{
			ps->_psnack = cur;
		}
		else
		{
			cur->next = ps->_psnack;
			ps->_psnack = cur;
		}

	}
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", Body);
		cur = cur->next;
	}
	cur= ps->_psnack;

	//设置蛇的相关信息
	ps->_dir = RIGHT;
	ps->_food_weight = 10;
	ps->_sleep_time = 200;
	ps->_status = OK;
	color(7);
}

void CreateFood(pSnack ps)
{
	int x = 0;//2-54
	int y = 0;//1-25
	again:
	do{
		x = rand() % 53 + 2;
		y = rand() % 24 + 1;
	} while (x%2!=0);

	//检测改坐标是否与蛇身重合 
	pSnackNode cur = ps->_psnack;
	while (cur)
	{
		if (cur->x == x && cur->y == y)
		{
			goto again;
		}
		cur = cur->next;
	}
	pSnackNode SnackFood = (pSnackNode)malloc(sizeof(SnackNode));
	if (SnackFood == NULL)
	{
		perror("CreateFood malloc fail");
		return;
	}
	color(13);
	SnackFood->x = x;
	SnackFood->y = y;
	SnackFood->next = NULL;
	SetPos(x, y);
	wprintf(L"%lc", Food);
	ps->_pfood = SnackFood;
	color(7);
}

void Pause()
{
	while (1)
	{
		Sleep(200);
		if (KEY_PRESS(VK_SPACE))
		{
			break;
		}
	}
}

void GameStart(pSnack ps)
{
	system("mode con cols=100 lines=30");
	system("title 贪吃蛇");
	//1光标隐藏
	cursor_hide();

	//2.打印环境界面
	//第一个界面,欢迎
	// 第二个界面,介绍怎么操作游戏
	Welcome_game();//+3.功能介绍
	CreatMap();
	

	//创建蛇
	CreateSnack(ps);
	//创建食物
	CreateFood(ps);


	//SetPos(0, 29);
	//system("pause");
}

void PrintHelpInfo()
{
	color(15);
	SetPos(64, 10);
	wprintf(L"不能穿墙,不能咬到自己");
	SetPos(64, 12);
	wprintf(L"用 ↑.↓.←.→ 来控制蛇的移动");
	SetPos(64, 14);
	wprintf(L"按F3加速,F4减速");
	SetPos(64, 16);
	wprintf(L"按ESC正常退出游戏,按空格暂停游戏");
	SetPos(64, 18);
	wprintf(L"能力有限公司提供支持");
	SetPos(0, 29);
	//	system("pause");
	color(7);
}

bool Next_Is_Food(pSnackNode pn, pSnack ps)
{
	return (ps->_pfood->x == pn->x && ps->_pfood->y == pn->y);
}

void Eat_Food(pSnackNode pn, pSnack ps)
{
	ps->_pfood->next = ps->_psnack;
	ps->_psnack = ps->_pfood;
	free(pn);
	pn = NULL;
	pSnackNode cur = ps->_psnack;
	color(10);
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", Body);
		cur = cur->next;
	}
	color(7);
	ps->_sum_score += ps->_food_weight;
	//在重新生成食物
	CreateFood(ps);
	
}

void No_Food(pSnackNode pn, pSnack ps)
{
	pn->next = ps->_psnack;
	ps->_psnack = pn;
	pSnackNode cur = ps->_psnack;
	//打印出来五个了
	color(10);
	while (cur->next->next != NULL)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", Body);
		cur = cur->next;
	}
	color(7);
	//将第六个位置打印为空格
    //释放第六个
	SetPos(cur->next->x, cur->next->y);
	printf("  ");
	
	free(cur->next);
	//再将倒数第二个的next为NULL
	cur->next = NULL;
}

void Kill_By_Wall(pSnack ps)
{
	if(ps->_psnack->x == 0 || ps->_psnack->x == 56 ||
		ps->_psnack->y == 0 || ps->_psnack->y == 26)
	{
		ps->_status = KILL_BY_WALL;
	}
}

void Kill_By_Self(pSnack ps)
{
	pSnackNode cur = ps->_psnack -> next;
	while (cur)
	{
		if (cur->x == ps->_psnack->x && cur->y == ps->_psnack->y)
		{
			ps->_status = KILL_BY_SELF;
			break;
		}
		cur = cur->next;
	}
}

void SnackMove(pSnack ps)
{
	pSnackNode pNextNode = (pSnackNode)malloc(sizeof(SnackNode));
	if (pNextNode == NULL)
	{
		perror("SnackMove::malloc fail");
		return;
	}
	switch (ps->_dir)
	{
	case UP:
		pNextNode->x = ps->_psnack->x;
		pNextNode->y = ps->_psnack->y - 1;
		break;
	case DOWN:
		pNextNode->x = ps->_psnack->x;
		pNextNode->y = ps->_psnack->y + 1;
		break;
	case LEFT:
		pNextNode->x = ps->_psnack->x - 2;
		pNextNode->y = ps->_psnack->y;
		break;
	case RIGHT:
		pNextNode->x = ps->_psnack->x + 2;
		pNextNode->y = ps->_psnack->y;
		break;
	}
	//下一个位置是食物
	if (Next_Is_Food(pNextNode, ps))
	{
		Eat_Food(pNextNode,ps);
	}
	else
	{
		No_Food(pNextNode, ps);
	}

	//检测是否被撞墙死
	Kill_By_Wall(ps);

	//检测是否被撞自己死
	Kill_By_Self(ps);
}

void GameRun(pSnack ps)
{
	PrintHelpInfo();
	//SetPos(64, 10);
	do {
		SetPos(64, 6);
		wprintf(L"当前的总分数为:");
		printf("%d ", ps->_sum_score);
		SetPos(64, 8);
		wprintf(L"当前单个食物分数为:");
		printf("%d ", ps->_food_weight);

		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_SPACE))
		{
			Pause();
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			//正常退出游戏
			ps->_status = END_OK;
		}
		else if (KEY_PRESS(VK_F3))
		{
			//加速
			if (ps->_sleep_time > 80)
			{
				ps->_sleep_time -= 30;
				ps->_food_weight += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			//减速
			if (ps->_food_weight > 2)
			{
				ps->_sleep_time += 30;
				ps->_food_weight -= 2;
			}
		}

		//实现蛇的移动
		SnackMove(ps);
		Sleep(ps->_sleep_time);
	} while (ps->_status==OK);
	//移动
	//实施打印情况
}



void GameEnd(pSnack ps)
{
	SetPos(24, 12);
	switch (ps->_status)
	{
	case END_OK:
		wprintf(L"您主动结束游戏\n");
		break;
	case KILL_BY_WALL:
		wprintf(L"您撞到墙上,游戏结束\n");
		break;
	case KILL_BY_SELF:
		wprintf(L"您撞到了自己,游戏结束\n");
		break;
	}

	//释放蛇身的链表

	pSnackNode cur = ps->_psnack;
	while (cur)
	{
		pSnackNode del = cur;
		cur = cur->next;
		free(del);
	}
}

 test.c

#define  _CRT_SECURE_NO_WARNINGS

#include"snack.h"


void test()
{
	char ch;
	do {
		//创建贪吃蛇
		Snack snack = { 0 };
		GameStart(&snack);
		//运行游戏
		GameRun(&snack);
		//结束游戏 - 善后工作
		GameEnd(&snack);
		SetPos(20, 15);
		printf("再来一局吗?(Y/N):");
		ch = getchar();
		while (getchar() != '\n');
	} while (ch == 'Y' || ch == 'y');
	SetPos(0, 28);

}
int main()
{
	srand((unsigned int)time(NULL));
	setlocale(LC_ALL, "");

	test();
	return 0;
}

 snack.h

#define  _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<windows.h>
#include<stdbool.h>
#include<stdlib.h>
#include <locale.h>
#include<time.h>
#include<errno.h>
#include<assert.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 DIRECTION
{
	UP = 1,
	DOWN,
	LEFT,
	RIGHT,
};

//蛇的状态
enum GAME_STATUS
{
	OK,//正常
	KILL_BY_WALL,
	KILL_BY_SELF,
	END_OK,
};

typedef struct SnackNode
{
	int x;
	int y;
	//指向下一个结点
	struct SnackNode* next;
}SnackNode, * pSnackNode;

typedef struct Snack
{
	//蛇的头
	pSnackNode _psnack;
	pSnackNode _pfood;
	enum DIRECTION _dir;
	enum GAME_STATUS _status;
	int _food_weight;//一个食物的分数
	int _sum_score;//总成绩
	int _sleep_time;//蛇的速度,越小越快
}Snack,*pSnack;

//定位光标
void SetPos(int x, int y);

//游戏开始
void GameStart(pSnack ps);

//欢迎界面
void Welcome_game();

//绘制地图
void CreatMap();

//打印提示操作信息
void PrintHelpInfo();

//创建蛇
void CreateSnack(pSnack ps);

//创建食物
void CreateFood(pSnack ps);

//游戏暂停
void Pause();

//游戏正常运行
void GameRun(pSnack ps);

//贪吃蛇的移动
void SnackMove(pSnack ps);

//检查下一个坐标位置是否为食物
bool Next_Is_Food(pSnackNode pn,pSnack ps);

//吃食物
void Eat_Food(pSnackNode pn, pSnack ps);

//下一个位置不是食物,进行移动
void No_Food(pSnackNode pn, pSnack ps);

//检测是否被撞墙死
void Kill_By_Wall(pSnack ps);

//检测是否被撞自己死
void Kill_By_Self(pSnack ps);

//正常的游戏结束
void GameEnd(pSnack ps);

 



控制台程序的准备:

需要运用到API

本游戏运行的时候需要用到控制台主机,而不是终端,对应的修改步骤如下:

控制台程序名字修改:

把名字改为贪吃蛇,会更好,那么修改方式如下:

 参考:mode命令(mode | Microsoft Learn)

system("title 贪吃蛇");

 

 

游戏框架构建:

首先定义游戏界面的大小,定义游戏区行数和列数。

平常我们运⾏起来的黑框程序其实就是控制台程序 我们可以使用cmd命令来设置控制台窗⼝的⻓宽:设置控制台窗⼝的大小,100行,30列

system("mode con cols=100 lines=30");

此外,我们还需要结构体用于表示蛇与食物的结点信息。

typedef struct SnackNode
{
	int x;
	int y;
	//指向下一个结点
	struct SnackNode* next;
}SnackNode, * pSnackNode;

此外还有存放游戏蛇的信息与各个游戏相关信息,也需要用结构体封装起来存放:

typedef struct Snack
{
	//蛇的头
	pSnackNode _psnack;
	pSnackNode _pfood;
	enum DIRECTION _dir;
	enum GAME_STATUS _status;
	int _food_weight;//一个食物的分数
	int _sum_score;//总成绩
	int _sleep_time;//蛇的速度,越小越快
}Snack,*pSnack;

同样也需要存放蛇的状态,比如正常,撞墙死亡,撞自己死亡。

//蛇的状态
enum GAME_STATUS
{
	OK,//正常
	KILL_BY_WALL,
	KILL_BY_SELF,
	END_OK,
};

同样蛇的运行方向也需要用一个枚举来存放:

为了增加可读性,我们使用一个数字来定义方向,如向上为1;

//蛇的方向
enum DIRECTION
{
	UP = 1,
	DOWN,
	LEFT,
	RIGHT,
};


控制台屏幕上的坐标COORD:

COORD是WindowsAPI中定义的⼀个结构体,表⽰⼀个字符在控制台屏幕幕缓冲区上的坐标,坐标系 (0,0)的原点位于缓冲区的顶部左侧单元格。

隐藏光标:

隐藏光标比较简单,是运用到WIN 32 API,先通过etConsoleCursorInfo(hOutput, &CursorInfo);获取控制台光标信息,再隐藏控制台光标,设置控制台光标状态;

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

 光标跳转

光标跳转,也就是让光标跳转到获得标准输出设备的句柄,与隐藏光标的操作步骤类似,然后定位光标的位置,跳转到指定位置:

void SetPos(int x,int y)
{
	//获得标准输出设备的句柄
	HANDLE hOutput = NULL;
	hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

	//定位光标的位置,到pos
	COORD pos = { x,y };
	SetConsoleCursorPosition(hOutput, pos);
}

打印颜色设置:

 颜色设置函数的作用是,将此后输出的内容颜色都更为所指定的颜色,接收的参数c是颜色代码,十进制颜色代码表如下:

void color(int c)
{
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c); //颜色设置
	//注:SetConsoleTextAttribute是一个API(应用程序编程接口)
}

初始化界面:

第一步就为打印地图:

需要注意的地方就是:
  1. 在cmd窗口中一个小方块占两个单位的横坐标,一个单位的纵坐标。我们的墙使用宽字符进行对应的填充,
  2. 光标跳转函数SetPos接收的是光标将要跳至位置的横纵坐标。
 例如第一次的坐标就为 (i,j) 那么下一次坐标就为(i+2,j);

 宽字符打印准备:

1:需要引头文件:

#include <locale.h>

2:修改当前地区

	setlocale(LC_ALL, "");

 3:对应的字符

#define Wall L'□'
#define Body L'●'
#define Food L'★'

我设置的墙的颜色为红色,可以根据自己喜欢,自己根据上面的图给出的颜色进行调整。 

void CreatMap()
{
	color(12);
	//上
	for (int i = 0; i < 29; i++)
	{
		wprintf(L"%lc", L'□');
	}
	//下
	SetPos(0, 26);
	for (int i = 0; i < 29; i++)
	{
		wprintf(L"%lc", L'□');
	} 
	//左
	for (int i = 1; i <= 25; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", L'□');
	}
	//右
	for (int i = 1; i <= 25; i++)
	{
		SetPos(56, i);
		wprintf(L"%lc", L'□');
	}
	color(7);
}

初始化蛇与蛇的打印:

我默认开始蛇身加上蛇头一共五个结点大小:

最一开始蛇的坐标:

#define POS_X 24
#define POS_Y 5

我们的蛇是运用结构体,并运用的单链表来创造,那么我们打印只需要遍历就可以,还是比较简单的

初始化的代码如下:(蛇的颜色我设置的是绿色)

void CreateSnack(pSnack ps)
{
	color(10);
	//默认开始初始化为5个结点
	pSnackNode cur = NULL;
	for (int i = 0; i < 5; i++)
	{
		cur =(pSnackNode)malloc(sizeof(SnackNode));
		cur->next = NULL;
		cur->x = POS_X + i * 2;
		cur->y = POS_Y;
		if (ps->_psnack == NULL)//第一次头插
		{
			ps->_psnack = cur;
		}
		else
		{
			cur->next = ps->_psnack;
			ps->_psnack = cur;
		}

	}
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", Body);
		cur = cur->next;
	}
	cur= ps->_psnack;

	//设置蛇的相关信息
	ps->_dir = RIGHT;
	ps->_food_weight = 10;
	ps->_sleep_time = 200;
	ps->_status = OK;
	color(7);
}

随机创建食物:

随机在游戏区生成食物,需要对生成后的坐标进行判断,只有该位置为空才能在此生成食物,否则需要重新生成坐标。食物坐标确定后,需要对游戏区该位置的状态进行标记。

食物我设置的是紫色。可以根据自己爱好,设置自己喜欢的颜色。

void CreateFood(pSnack ps)
{
	int x = 0;//2-54
	int y = 0;//1-25
	again:
	do{
		x = rand() % 53 + 2;
		y = rand() % 24 + 1;
	} while (x%2!=0);

	//检测改坐标是否与蛇身重合 
	pSnackNode cur = ps->_psnack;
	while (cur)
	{
		if (cur->x == x && cur->y == y)
		{
			goto again;
		}
		cur = cur->next;
	}
	pSnackNode SnackFood = (pSnackNode)malloc(sizeof(SnackNode));
	if (SnackFood == NULL)
	{
		perror("CreateFood malloc fail");
		return;
	}
	color(13);
	SnackFood->x = x;
	SnackFood->y = y;
	SnackFood->next = NULL;
	SetPos(x, y);
	wprintf(L"%lc", Food);
	ps->_pfood = SnackFood;
	color(7);
}

蛇的单向移动:

移动蛇函数的作用就是先覆盖当前所显示的蛇,然后再打印移动后的蛇。

对应蛇尾的位置打印变为空格并删除一次蛇尾,然后再次创建一个新的蛇头,更换蛇头

void SnackMove(pSnack ps)
{
	pSnackNode pNextNode = (pSnackNode)malloc(sizeof(SnackNode));
	if (pNextNode == NULL)
	{
		perror("SnackMove::malloc fail");
		return;
	}
	pNextNode->x = ps->_psnack->x + 2;
	pNextNode->y = ps->_psnack->y;

	pn->next = ps->_psnack;
	ps->_psnack = pn;
	pSnackNode cur = ps->_psnack;
	//打印出来五个了
	while (cur->next->next != NULL)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", Body);
		cur = cur->next;
	}
	SetPos(cur->next->x, cur->next->y);
	printf("  ");
}


大致小部分已经实现完成,那么就利用游戏逻辑来实现剩余的代码;

在玩贪吃蛇的时候,我们知道

在蛇的移动过程,我们可以通过按键修改蛇的移动方向,来进行转弯,而且在移动的过程中,我们还可以随时改变速度,来改变游戏的难度,来增加游戏的可玩性,在行动的过程中,不免会撞墙,撞自己,吃到了食物,没有吃到食物,等等各种不同的情况,那么 对于实现的逻辑就是如上:

那么我先修改蛇的移动使其可以更换方向

在修改蛇的方向前,我们知道我们是通过按键来改变,那么我们就需要通过某种方法得知我们按了什么键来进行修改方向,同样也是API的知识

//检查某个按键是否被按了
#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )

添加方向的改变与判断蛇的各个状态判断:

bool Next_Is_Food(pSnackNode pn, pSnack ps)
{
	return (ps->_pfood->x == pn->x && ps->_pfood->y == pn->y);
}
void SnackMove(pSnack ps)
{
	pSnackNode pNextNode = (pSnackNode)malloc(sizeof(SnackNode));
	if (pNextNode == NULL)
	{
		perror("SnackMove::malloc fail");
		return;
	}
	switch (ps->_dir)
	{
	case UP:
		pNextNode->x = ps->_psnack->x;
		pNextNode->y = ps->_psnack->y - 1;
		break;
	case DOWN:
		pNextNode->x = ps->_psnack->x;
		pNextNode->y = ps->_psnack->y + 1;
		break;
	case LEFT:
		pNextNode->x = ps->_psnack->x - 2;
		pNextNode->y = ps->_psnack->y;
		break;
	case RIGHT:
		pNextNode->x = ps->_psnack->x + 2;
		pNextNode->y = ps->_psnack->y;
		break;
	}
	//下一个位置是食物
	if (Next_Is_Food(pNextNode, ps))
	{
		Eat_Food(pNextNode,ps);
	}
	else
	{
		No_Food(pNextNode, ps);
	}

	//检测是否被撞墙死
	Kill_By_Wall(ps);

	//检测是否被撞自己死
	Kill_By_Self(ps);
}

 对该函数里面各个小函数进行代码展示:

这里面的小函数都是比较好实现的,这里就不在解释:

Eat_Food(pNextNode,ps);

 需要注意的是吃完这个食物后,要记得重新随机创建食物

void Eat_Food(pSnackNode pn, pSnack ps)
{
	ps->_pfood->next = ps->_psnack;
	ps->_psnack = ps->_pfood;
	free(pn);
	pn = NULL;
	pSnackNode cur = ps->_psnack;
	color(10);
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", Body);
		cur = cur->next;
	}
	color(7);
	ps->_sum_score += ps->_food_weight;
	//在重新生成食物
	CreateFood(ps);
}

void No_Food(pSnackNode pn, pSnack ps)

 要记得把尾打印改为空格

void No_Food(pSnackNode pn, pSnack ps)
{
	pn->next = ps->_psnack;
	ps->_psnack = pn;
	pSnackNode cur = ps->_psnack;
	//打印出来五个了
	color(10);
	while (cur->next->next != NULL)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", Body);
		cur = cur->next;
	}
	color(7);
	//将第六个位置打印为空格
    //释放第六个
	SetPos(cur->next->x, cur->next->y);
	printf("  ");
	
	free(cur->next);
	//再将倒数第二个的next为NULL
	cur->next = NULL;
}

void Kill_By_Wall(pSnack ps)

void Kill_By_Wall(pSnack ps)
{
	if(ps->_psnack->x == 0 || ps->_psnack->x == 56 ||
		ps->_psnack->y == 0 || ps->_psnack->y == 26)
	{
		ps->_status = KILL_BY_WALL;
	}
}

void Kill_By_Self(pSnack ps)

void Kill_By_Self(pSnack ps)
{
	pSnackNode cur = ps->_psnack -> next;
	while (cur)
	{
		if (cur->x == ps->_psnack->x && cur->y == ps->_psnack->y)
		{
			ps->_status = KILL_BY_SELF;
			break;
		}
		cur = cur->next;
	}
}

最后,还有对应的就是运行是代码的逻辑展示

void GameRun(pSnack ps)
{

	//SetPos(64, 10);
	do {


		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_SPACE))
		{
			Pause();
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			//正常退出游戏
			ps->_status = END_OK;
		}
		else if (KEY_PRESS(VK_F3))
		{
			//加速
			if (ps->_sleep_time > 80)
			{
				ps->_sleep_time -= 30;
				ps->_food_weight += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			//减速
			if (ps->_food_weight > 2)
			{
				ps->_sleep_time += 30;
				ps->_food_weight -= 2;
			}
		}

		//实现蛇的移动
		SnackMove(ps);
		Sleep(ps->_sleep_time);
	} while (ps->_status==OK);
	//移动
	//实施打印情况
}

速度的控制(单位毫秒):

 

void Pause()
{
	while (1)
	{
		Sleep(200);
		if (KEY_PRESS(VK_SPACE))
		{
			break;
		}
	}
}

 最后的游戏收尾就是提示信息的打印:

void GameEnd(pSnack ps)
{
	SetPos(24, 12);
	switch (ps->_status)
	{
	case END_OK:
		wprintf(L"您主动结束游戏\n");
		break;
	case KILL_BY_WALL:
		wprintf(L"您撞到墙上,游戏结束\n");
		break;
	case KILL_BY_SELF:
		wprintf(L"您撞到了自己,游戏结束\n");
		break;
	}

	//释放蛇身的链表

	pSnackNode cur = ps->_psnack;
	while (cur)
	{
		pSnackNode del = cur;
		cur = cur->next;
		free(del);
	}
}


最后一步便是锦上添花了,就是打印提示信息:

 

void PrintHelpInfo()
{
	color(15);
	SetPos(64, 10);
	wprintf(L"不能穿墙,不能咬到自己");
	SetPos(64, 12);
	wprintf(L"用 ↑.↓.←.→ 来控制蛇的移动");
	SetPos(64, 14);
	wprintf(L"按F3加速,F4减速");
	SetPos(64, 16);
	wprintf(L"按ESC正常退出游戏,按空格暂停游戏");
	SetPos(64, 18);
	wprintf(L"能力有限公司提供支持");
	SetPos(0, 29);
	//	system("pause");
	color(7);
}

到这里就已经完成了,一共有三个页面:

 第一个是又是封面,第二个为提示信息,第三个是游戏运行界面

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/618416.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

DELL T630服务器iDRAC分辨率调整办法

对于Dell T630服务器的iDRAC分辨率调整&#xff0c;您需要登录到iDRAC的Web界面。以下是详细的步骤&#xff1a; 登录iDRAC&#xff1a;在浏览器中输入iDRAC的IP地址&#xff0c;然后使用用户名&#xff08;通常是“root”&#xff09;和密码登录。 导航到虚拟控制台&#xff…

(Java)心得:LeetCode——19.删除链表的倒数第 N 个节点

一、原题 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示例 2&#xff1a; 输入&#xff1a;head [1], n 1 输出&#xff1a;[]示例 3&…

IEEE 802.11标准

在IEEE 802.11标准中使用了扩频通信技术&#xff0c;主要作用是使得抗干扰性更强。 IEEE 802.11在MAC层采用了CSMA/CA协议。 IEEE 802.1x是一种基于端口认证协议。

报表-接口类型的数据源

1、配置 在数据中进行如下配置 配置格式&#xff0c;换行的方式 #API $.data[0].children http://192.168.1.1:9200/apis/getInfo 行1&#xff1a;固定写法&#xff0c;标识这是一个接口类型的数据集 行2&#xff1a;JSONPath格式字符串&#xff0c;对接口的数据进行取值。…

【半个月我拿下了软考证】软件设计师高频考点--系统化教学-关系模式

&#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;软件设计师考点暴击 ⭐&#x1f170;️进入狂砍分⭐ ⭐软件设计师高频考点文档&#xff0c; ⭐软件设计师高频考点专栏 ⭐软件设计师高频考点⭐ &#x1f3b6;&#xff08;A) 考点1,关系模式 考点&#xff1a; 三个模式相…

kettle经验篇:MongoDB-delete插件问题

目录 项目场景 问题分析 解决方案 MongoDB Delete插件使用总结 项目场景 项目使用的ODS层数据库是MongoDB&#xff1b;在数据中心从DB层向ODS层同步数据过程中&#xff0c;发现有张ODS表在同步过程中&#xff0c;数据突然发生锐减&#xff0c;甚至于该ODS表数据清0。 同步…

Zabbix6.0容器化部署(Docker-Composed)

Zabbix 为每个 Zabbix 组件提供 Docker image 作为可移植和自给自足的容器&#xff0c;以加快部署和更新过程。 Zabbix 组件在 Ubuntu、Alpine Linux 和 CentOS 基础 image 上提供:Zabbix 组件支持 MySQL 和 PostgreSQL 数据库、Apache2 和 Nginx Web 服务器。 1. Zabbix 组件…

17 SPI FLASH读写

SPI 协议简介 SPI 即 Serial Periphera linterface 的缩写&#xff0c;顾名思义就是串行外围设备接口&#xff0c;主要用于与FLASH、实时时钟、AD 转换器等外设模块的通信&#xff0c;它是一种高速的全双工同步的通信总线。 SPI 设备分为主设备和从设备&#xff0c;SPI 通信必…

Pikachu 靶场 RCE 通关解析

前言 Pikachu靶场是一种常见的网络安全训练平台&#xff0c;用于模拟真实世界中的网络攻击和防御场景。它提供了一系列的实验室环境&#xff0c;供安全专业人士、学生和爱好者练习和测试他们的技能。 Pikachu靶场的目的是帮助用户了解和掌握网络攻击的原理和技术&#xff0c;…

mybatis-plus使用指南(1)

快速开始 首先 我们 在创建了一个基本的springboot的基础框架以后&#xff0c;在 pom文件中 引入 mybatisplus的相关依赖 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5…

sumif的求和区域是文本格式怎么办?

sumif函数的求和区域是文本型数字&#xff0c;不更改源数据的情况下怎么求和呢&#xff1f; 一、不能使用SUMIF、SUMIFS函数 这两个函数的求和区域只能是引用&#xff0c;不能是公式运算的内存数组&#xff0c;因此不能用公式或运算符将求和区转换成数值。当引用来的数据是文本…

【C/C++】C/C++ 校园失物招领系统设计与实现(源码+报告)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

一套MySQL读写分离分库分表的架构,被秀到了!

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 作者&#xff1a;IT邦德 中国DBA联盟(ACDU)成员&#xff0c;10余年DBA工作经验&#xff0c; Oracle、PostgreSQL ACE CSDN博客专家及B站知名UP主&#xff0c;全网粉丝10万 擅长主流Oracle、My…

算法设计与分析 例题 绘制Huffman树、循环赛、分治、最短路与动态规划

1.考虑用哈夫曼算法来找字符a,b,c,d,e,f 的最优编码。这些字符出现在文件中 的频数之比为 20:10:6:4:44:16。要求&#xff1a; &#xff08;1&#xff09;&#xff08;4 分&#xff09;简述使用哈夫曼算法构造最优编码的基本步骤&#xff1b; &#xff08;2&#xff09;&…

Java数据结构---栈和队列

目录 栈&#xff08;Stack&#xff09; 队列&#xff08;Queue&#xff09; 循环队列 栈&#xff08;Stack&#xff09; 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除操作元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一…

2024最新商业视频打赏系统源码 多套模板 有代理后台 已对接支付

简介&#xff1a; 2024最新商业视频打赏系统源码 多套模板 有代理后台 已对接支付 图片&#xff1a; 源码下载

IC-Light-在stable diffusion中实现图像的光影控制新方法 - 技术原理篇

&#x1f468;背景与来源 最近在stable diffusion的粉丝群看到光影控制又有了新的玩法&#xff0c;是controlnet的作者lllyasviel&#xff0c;发了一款名为IC-Light的模型&#xff0c;并且已经被另外一位名为huchenlei的朋友实现了comfyui和webUI&#xff08;forge &#xff0…

事件高级部分

一&#xff0c;注册事件 即给元素添加事件 1.传统注册方式 2.方法监听注册方式 事件类型&#xff1a;字符串形式&#xff0c;不用带on 可以给一个元素添加多个程序 二.删除事件 1.方式 参数见上文 三.DOM事件流 事件的传播过程叫做事件流 js代码只能获取一个阶段&#xf…

【考研数学】汤家凤“免单“数学题被吐槽‘太难’,老汤回应「怎么还有脸笑」,网友:这些题有毒!

我看了汤家凤老师出的几道题&#xff0c;实际上对于考研的同学来说&#xff0c;确实是送分题 第一个是三角函数变换中的万能公式&#xff1b;第二个e^x的泰勒展开公式&#xff1b;第三个是第一类重要极限。只要复习过&#xff0c;那基本上都能正常做出来。 至于汤家凤老师说「…

STM32快速入门(总线协议之I2C一主多从(软件实现 硬件实现))

STM32快速入门&#xff08;总线协议之I2C一主多从&#xff08;软件实现 & 硬件实现&#xff09;&#xff09; 前言 支持一对多&#xff08;一主多从&#xff09;、多对多传输&#xff08;多主多从&#xff09;&#xff0c;只支持半双工&#xff0c;一般有两根数据线&…