会加速且贪吃的美杜莎女王(C语言代码超详解)

本篇会加入个人的所谓‘鱼式疯言’
❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言
而是理解过并总结出来通俗易懂的大白话,
我会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的.
可能说的不是那么严谨.但小编初心是能让更多人能接受我们这个概念 !!

会加速且贪吃的美杜莎女王(C语言代码超详解)_第1张图片

前言

亲爱的,你可能只是玩过普通移动在这里插入图片描述吧,但友子们应该很少见过能加速和减速,能加额外分和减去分值的贪吃蛇小游戏吧!!!
友子们居然点进来来,就别着急走哦,今天小编就详细的讲解我们是怎么用C语言实现我们想要的贪吃蛇游戏的。

目录

1.贪吃蛇游戏功能介绍
2.游戏环境介绍
3. 游戏设计与分析 (●’◡’●) (●’◡’●) (●’◡’●)
4. 核心逻辑实现分析 (/ω\) (/ω\) (/ω\)
5. 参考代码

一.贪吃蛇游戏功能介绍

使用C语言在Windows环境的控制台中模拟实现经典小游戏贪吃蛇
基本的功能:
• 贪吃蛇地图绘制
• 蛇吃食物的功能 (上、下、左、右方向键控制蛇的动作)
• 蛇撞墙死亡
• 蛇撞自身死亡
• 计算得分
• 蛇身加速、减速
• 暂停游戏

二.游戏环境介绍

当宝子们要写贪吃蛇游戏时,我们不禁想一个问题,我们要在哪个运行环境来执行我们的代码程序呢

<1>.控制台程序

平常我们运行起来的黑框程序其实就是控制台程序
我们可以使用cmd命令来执行控制台功能

1.设置控制台窗口的大小和名字,40行,125列
贪吃蛇

//设置控制台大小和标题
system("mode con cols=125 lines=40");
system("title 贪吃蛇小游戏");

2.隐藏光标操作

void TrandCUrsor()
//隐藏光标
{
	//去除光标
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	//得到句手

	CONSOLE_CURSOR_INFO CursorInfo;
	//设置光标结构体变量

	GetConsoleCursorInfo(handle, &CursorInfo);
	//得到光标信息

	CursorInfo.bVisible = false;
	//隐藏光标

	SetConsoleCursorInfo(handle, &CursorInfo);
	//操作光标


}

3.设置控制台屏幕上的坐标COORD

void setpos(int x, int y)
//设置光标位置
{
	HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
	//得到句手

	COORD pos = { x,y };
	//输入想要的坐标
	
	SetConsoleCursorPosition(handle, pos);
	//执行所在的坐标
}

4.设置本地化的状态

这时有宝子就要问啦,为啥要设置本地化状态呢
因为我们后期要打印相关的蛇身,蛇身的每个元素就需要迁移到本地来操作,如果还有 疑惑,友友们不用着急哦,小编这就细细到来
这时就需要设置相关的 本地化!!!

setlocale(LC_ALL, "");
//实现本地化,对应输入空字符

最终效果展示
会加速且贪吃的美杜莎女王(C语言代码超详解)_第2张图片

鱼式疯言

什么?
爱提问的小爱同学居然问控制台程序怎么设计,他居然不是???

小编想说:小爱同学问的好的,有时候我们的编译器不是控制台程序,那怎么办呢,请听小编 一 一 道来

会加速且贪吃的美杜莎女王(C语言代码超详解)_第3张图片

会加速且贪吃的美杜莎女王(C语言代码超详解)_第4张图片

宝子们按照上面做就可以啦

三 . 游戏设计与分析

这时我们就需要设置三个游戏的步骤

1.游戏开始的准备页面

2.游戏进行的维护页面

3,游戏结束的清理页面

会加速且贪吃的美杜莎女王(C语言代码超详解)_第5张图片
于是我们就有了一下这些

void Test()
{


	int n = 0;
	do
	{
		Snoke snoke = { 0 };
		//蛇身初始
		
		GameInit(&snoke);
		//游戏开始
		GameContin(&snoke);
		//游戏过程

		GameOver(&snoke);
		//游戏结束

		setpos(15, 14);
		printf("亲,再来一局么(Y/N):");
		n = getchar();
		getchar();
		//设置是否重来一局

	} while (n == 'Y' || n == 'y');
	
	setpos(0, 32);
	//-退出提示的打印到最后几行

}

四.核心逻辑实现分析

1.游戏开始的准备

<1>.地图和欢迎页面的打印

void setwelcome()
//打印欢迎界面
{
	setpos(46, 15);

	printf("各位王子 ,公主殿下");

	setpos(46, 17);

	printf("欢迎进入贪吃蛇小游戏!");

	setpos(49, 30);
	system("pause");
	system("cls");

	setpos(40, 15);
	printf("亲,请按 ↑ ↓  ←  → 来控制蛇的移动");
	setpos(40, 17);
	printf("F3为加速,F4为减速");
	setpos(40, 19);
	printf("加速可以得到更高分");
	setpos(49, 30);
	system("pause");
	system("cls");

}

会加速且贪吃的美杜莎女王(C语言代码超详解)_第6张图片
会加速且贪吃的美杜莎女王(C语言代码超详解)_第7张图片

void Printmap()
//打印地图
{

	//上
	setpos(0, 0);
	//先定位第一行第一列

	int i = 0;
	for ( i = 0; i <=56; i+=2)
	//上方墙两个字符空间为一个宽字符字符
	{
		wprintf(L"%lc", WALL);
		//打印墙
	}

	//下
	setpos(0, 26);
	//最后一行即为下
	for ( i = 0; i <= 56; i += 2)
	//下方墙两个字符空间为一个宽字符字符
	{
		wprintf(L"%lc", WALL);
	}


	//左
	for ( i = 0; i <=25; i++)
	{
		setpos(0, i);
		//每次打印就要移动到下一行
		wprintf(L"%lc", WALL);
	}

	//右
	for (i = 0; i <= 25; i++)
	{
		setpos(56, i);
		wprintf(L"%lc", WALL);
	}

	setpos(49, 30);
	

}

<2>.蛇身的打印

void SnokeInit(pSnoke ps)
//蛇身的初始化与打印
{
	ps->pFood = ps->pSnoke = NULL;
	//食物和蛇头置为NULL
	
	ps->Sorce = 0;
	//得分清楚为0
	ps->FoodWeight = 10;
	//食物初始得分10

	ps->SleepTime = 200;
	//休眠时间200
	//决定着蛇的初始速度
	
	ps->Dir = RIGHT;
	//方向向右
	
	ps->status = OK;
	//状态正常

	pSnoke cur = NULL;
	int i = 0;

	for (i = 0; i < 5; i++)
	{
		pSnakeNode cat = (pSnakeNode)malloc(sizeof(SnakeNode));
		//创建蛇身
		if (cat == NULL)
		//检查是否创建成功
		{
			perror("SnokeInit()::malloc()");
			return;
		}

		cat->x = POS_X + 2 * i;
		//初始的长度一直往右走
		cat->y = POS_Y;
		cat->next=NULL;

		if (ps->pSnoke == NULL)
		//蛇头为NULL.就先放蛇头节点
		{
			ps->pSnoke = cat;
		}
		else
		//放完之后,蛇头下一个节点继续放
		{
			cat->next = ps->pSnoke;
			ps->pSnoke = cat;
		}
	}

	pSnakeNode stu = ps->pSnoke;
	while (stu)
	//最终打印
	{
		setpos(stu->x, stu->y);
		wprintf(L"%lc", BODY);
		stu = stu->next;
	}
	
}

<3>.食物打印

void CreatFood(pSnoke ps)
//食物的初始化与打印
{
	int x = 0;
	int y = 0;
	//初始化预定值

again:
	do
	{
		x = rand() % 53 + 2;
		y = rand() % 24 + 1;
	} while (x%2!=0);
	//跳出的是偶数就是我们想要的值

	pSnakeNode stu = ps->pSnoke;
	//给个头来遍历链表


	while (stu)
	//遍历我们每一个蛇身的坐标
	{
		if (stu->x == x && stu->y == y)
		//在遍历蛇身坐标的过程中
		//一旦蛇身坐标等于该食物坐标就goto跳回
		{
			goto again;
			//跳到上面的do...while循环
		}

		stu = stu->next;

	}

	pSnakeNode cur = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (cur == NULL)
	{
		perror("CreatFood()::malloc()");
		return;
	}

	cur->x = x;
	cur->y = y;
	ps->pFood = cur;
	//坐标附上
	//食物的节点同时也接上

	setpos(cur->x, cur->y);
	wprintf(L"%lc", FOOD);
	//给定随机生成的坐标并打印
	
}

会加速且贪吃的美杜莎女王(C语言代码超详解)_第8张图片

3.打印提示信息

void printInfo(pSnoke ps)
//打印提示信息
{
	setpos(70, 15);
	printf("亲,请按 ↑ ↓  ←  → 来控制蛇的移动");
	
	setpos(70, 17);
	printf("不能穿墙,不能咬到自己");

	

	setpos(70, 19);
	printf("F3 为加速,F4 为减速");
	setpos(70, 21);
	printf("ESC 为退出,空格 为暂停");

	setpos(70, 23);
	printf("吃鱼专属版本");

	setpos(70, 25);
	printf("有更优化的大佬可以加我wx:zcy3075678575 欢迎指点在下");
}

会加速且贪吃的美杜莎女王(C语言代码超详解)_第9张图片

2.游戏进行的维护

那我们的计算机是怎么感应我们键盘按下的相对应健的信息的呢
这是我们就需要用到我们window自身系统函数的调用

#define KEY_PRESS(VK)  ((GetAsyncKeyState(VK) & 0x1) ? 1 : 0)
//设置感应键

<1>.这样我们就可以分装按键识别的函数

void Touchboard(pSnoke ps)
//按键识别
{
	do
	{
		setpos(70, 8);
		printf("每个食物的分数为:%02d  总分为:%3d", ps->FoodWeight, ps->Sorce);
		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))
		//感应ESC键
		{
			ps->status = ESC;
			//ESC键赋值
			
			break;
			//break可有可无
			//一旦状态不为OK就会跳出
		}
		else if (KEY_PRESS(VK_SPACE))
		//感应空格键
		{
			pause();
			//分装暂停函数
		}
		else if (KEY_PRESS(VK_F3))
		//感应F3
		{
			if (ps->SleepTime >= 80)
			//最快的休眠时间限制
			{
				ps->SleepTime -= 30;
				//时间减30
				ps->FoodWeight += 2;
				//分数加2
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			if (ps->FoodWeight > 2)
			{
				ps->SleepTime += 30;
				//时间加长
				ps->FoodWeight -= 2;
				//分数减2
			}
		}

		Sleep(ps->SleepTime);
		//每次调整蛇的休眠
		//休眠时间即蛇的速度

		SnakeWalk(ps);
		//蛇的行走
	} 
	while (ps->status == OK);
	//主要游戏运行过程在这里
	//以ps->status 的 OK 状态进行游戏的循环进行
	
}

<2> .控制蛇的移动

void SnakeWalk(pSnoke ps)
//蛇的行走
{
	pSnakeNode pNext = (pSnakeNode)malloc(sizeof(SnakeNode));
	//创建个预先要前进的节点
	if (pNext==NULL)
	//判断是否创建成功
	{
		perror("SnakeWalk()::malloc()");
		return;
	}

	pNext->next = NULL;
	//先置NULL,判断是否下一个节点是食物
	switch (ps->Dir)
	{
	case UP:
		pNext->x = ps->pSnoke->x;
		pNext->y = ps->pSnoke->y-1;
		//向上走,横坐标不变,纵坐标减1
		break;
	case DOWN:
		pNext->x = ps->pSnoke->x;
		pNext->y = ps->pSnoke->y + 1;
		//向下走,横坐标不变,纵坐标加1

		break;
	case LEFT:
		pNext->x = ps->pSnoke->x - 2;
		//向左走,横坐标减去一个宽字符的长度.纵坐标不变
		pNext->y = ps->pSnoke->y;
		break;
	case RIGHT:
		pNext->x = ps->pSnoke->x + 2;
		//向右走,横坐标加上一个宽字符的长度.纵坐标不变

		pNext->y = ps->pSnoke->y;
		break;
	}

	NextIsFood(ps, pNext);
	//判断下一个是不是食物

	KillByWall(ps);
	//被墙撞死

	KillBySelf(ps);
	//咬到自己

}

<3>.判断下一个点是否是食物

void NextIsFood(pSnoke ps,pSnakeNode pNext)
//判断下一个是不是食物并打印
{
	if (pNext->x == ps->pFood->x && pNext->y == ps->pFood->y)
	//如果蛇的坐标和食物的坐标重合
	//就可以判断蛇吃到了食物
	{
		pNext->next = ps->pSnoke;
		//新的节点为蛇头连接蛇身
		ps->pSnoke = pNext;
		
		pSnakeNode tre = ps->pSnoke;
		
		while (tre)
		//循环打印过程
		{
			setpos(tre->x, tre->y);
			wprintf(L"%lc", BODY);
			tre = tre->next;
		}

		ps->Sorce += ps->FoodWeight;
		//计算上总分
		
		free(ps->pFood);
		//释放该食物节点
		CreatFood(ps);
		//创建新的食物
	}
	else
	{
		pNext->next = ps->pSnoke;
		ps->pSnoke = pNext;

		pSnakeNode tre = ps->pSnoke;
		//从头开始找.遇到最后一个节点的前一个节点就跳出

		while (tre->next->next )
		{

			setpos(tre->x, tre->y);
			wprintf(L"%lc", BODY);
			//一般遍历向下找
			// 一边打印身体
			tre = tre->next;
		}

		setpos(tre->x, tre->y);
		//遗留下最后一个节点
		wprintf(L"%lc", BODY);
		
		setpos(tre->next->x, tre->next->y);
		//最后一个节点打印空格
		printf("  ");

		free(tre->next);
		//最终要销毁蛇的最后一个节点
		tre->next = NULL;
		//形成移动的过程
	}
	
}

鱼式疯言

虽然这里没有游戏页面的展示,但好戏都是压轴哒,友友们耐点心哦

惊喜都在后面

<4>.空格功能的分装

void pause()
//分装空格暂停
{
	while (1)
	//无限循环
	{
		Sleep(100);
		//不断休眠
		
		if (KEY_PRESS(VK_SPACE))
		//一旦感应到就跳出休眠
			break;
	}
}

会加速且贪吃的美杜莎女王(C语言代码超详解)_第10张图片

3.游戏结束的清理

游戏结束有三种情况,有可能撞墙死,有可能咬到自己,或者是自己退出.

<1>.撞墙死亡

void KillByWall(pSnoke ps)
//被墙撞死
{
	if (ps->pSnoke->x == 0 
		|| ps->pSnoke->x==56
		||ps->pSnoke->y==0
		||ps->pSnoke->y==26)
	//一旦头节点越级到墙边就说明撞墙了
	{
		ps->status = KILL_BY_WALL;
		//状态改变为撞墙了
	}

}

会加速且贪吃的美杜莎女王(C语言代码超详解)_第11张图片

<2>.咬到自己死亡

void KillBySelf(pSnoke ps)
//咬到自己
{
	pSnakeNode stu = ps->pSnoke->next;
	//先给个头节点的下一个节点
	while (stu)
	//开始遍历
	{
		if (stu->x == ps->pSnoke->x && stu->y == ps->pSnoke->y) 
		//一但头咬到身体的任何一个部位,就状态跳出
		{
			ps->status = KILL_BY_SELF;
			return;
		}

		stu = stu->next;
	}
}

会加速且贪吃的美杜莎女王(C语言代码超详解)_第12张图片

<3>. ESC正常退出

会加速且贪吃的美杜莎女王(C语言代码超详解)_第13张图片

<4>. 销毁蛇身和食物节点

void GameOver(pSnoke ps)
//游戏结束
{
	setpos(15, 10);
	//打印游戏怎么结束的提示信息

	//这里也可以用 switch 来判断更方便
	//枚举常量也是整型开始的
	if (ps->status == KILL_BY_SELF)
	//咬到自己了
	{
		
		printf("很遗憾!咬到自己了,游戏结束...");
	}
	else if(ps->status==KILL_BY_WALL)
	//撞墙了
	{
		
		printf("很遗憾!撞墙了,游戏结束...");
	}
	else if (ps->status==ESC)
	//ESC退出了
	{
		
		printf("游戏正在退出中...");
	}
	
	pSnakeNode str = ps->pSnoke;
	//头节点
	pSnakeNode del = NULL;
	//双指针
	while (str)
	{
		del = str;
		
		str = str->next;
		//先赋值,再释放
		free(del);
	}

	ps->pSnoke = NULL;
	free(ps->pFood);
	//没有结束的食物也要释放掉
	
	ps->pFood = NULL;
	//食物和蛇身都要置NULL
}

会加速且贪吃的美杜莎女王(C语言代码超详解)_第14张图片

五.参考代码

<1>.头文件 snake,h

#pragma once

#define _CRT_SECURE_NO_WARNINGS

//定义蛇的元素
#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)
//设置感应键

#include
#include
#include
#include
#include
#include


typedef struct SnakeNode
//贪吃蛇节点的定义
{
	int x;
	int y;
	struct SnakeNode* next;
}SnakeNode, *pSnakeNode;

enum GAME_STATUS
//游戏状态枚举
{
	OK=1,
	//正常运行
	ESC,
	//正常退出

	KILL_BY_WALL,
	//撞墙了

	KILL_BY_SELF
	//咬到自己
};

enum DRECTION
//蛇移动的方向
{
	UP=1,
	//上

	DOWN,
	//下

	LEFT,
	//左

	RIGHT
	//右
};

typedef struct Snoke
//贪吃蛇
{
	pSnakeNode pSnoke; //维护整条蛇的指针
	pSnakeNode pFood;   //指向食物的指针
	int FoodWeight;    //每个食物的分数
	int Sorce;         //总得分
	int SleepTime;     //蛇的休眠时间即蛇的速度
	enum GAME_STATUS status;   //游戏状态
	enum DRECTION Dir;         //蛇的移动
}Snoke ,*pSnoke;


void GameInit();
//游戏开始


void TrandCUrsor();
//隐藏光标


void setpos(int x, int y);
//设置光标位置

void setwelcome();
//打印欢迎界面

void Printmap();
//打印地图

void SnokeInit(pSnoke ps);
//蛇身的初始化与打印


void CreatFood(pSnoke ps);
//食物的初始化与打印


void printInfo(pSnoke ps);
//打印提示信息

void GameContin(pSnoke ps);
//游戏过程

void pause();
//封装空格暂停


void Touchboard(pSnoke ps);
//按键识别

void SnakeWalk(pSnoke ps);
//蛇的行走


void NextIsFood(pSnoke ps);
//判断下一个是不是食物



void KillByWall(pSnoke ps);
//被墙撞死

void KillBySelf(pSnoke ps);
//咬到自己

void GameOver(pSnoke ps);
//游戏结束

<2>.逻辑文件 test.c

#include "snake.h"


void Test()
{


	int n = 0;
	do
	{
		Snoke snoke = { 0 };
		//蛇身初始
		
		GameInit(&snoke);
		//游戏开始
		GameContin(&snoke);
		//游戏过程

		GameOver(&snoke);
		//游戏结束

		setpos(15, 14);
		printf("亲,再来一局么(Y/N):");
		n = getchar();
		getchar();
		//设置是否重来一局

	} while (n == 'Y' || n == 'y');
	
	setpos(0, 32);
	//-退出提示的打印到最后几行

}


int main()
{
	srand((unsigned)time(NULL));
	//设置随机数种子

	setlocale(LC_ALL, "");
	//实现本地化,可以输出空字符
		
	Test();
	return 0;
}

<3>,游戏文件 snake.c

#include "snake.h"




void GameInit(pSnoke ps)
//设置初始页面
{
	

	//设置控制台大小和标题
	system("mode con cols=125 lines=40");
	system("title 贪吃蛇小游戏");

	TrandCUrsor();
	//隐藏光标

	setwelcome();
	//打印欢迎界面

	Printmap();
	//打印地图

	SnokeInit(ps);
	//蛇的初始化

	CreatFood(ps);
	//食物的初始化和打印

	printInfo(ps);
	//打印提示信息

	
}



void GameContin(pSnoke ps)
{
	Touchboard(ps);
	//按键识别以及蛇的行走过程
}


void GameOver(pSnoke ps)
//游戏结束
{
	setpos(15, 10);
	//打印游戏怎么结束的提示信息

	//这里也可以用 switch 来判断更方便
	//枚举常量也是整型开始的
	if (ps->status == KILL_BY_SELF)
	//咬到自己了
	{
		
		printf("很遗憾!咬到自己了,游戏结束...");
	}
	else if(ps->status==KILL_BY_WALL)
	//撞墙了
	{
		
		printf("很遗憾!撞墙了,游戏结束...");
	}
	else if (ps->status==ESC)
	//ESC退出了
	{
		
		printf("游戏正在退出中...");
	}
	
	pSnakeNode str = ps->pSnoke;
	//头节点
	pSnakeNode del = NULL;
	//双指针
	while (str)
	{
		del = str;
		
		str = str->next;
		//先赋值,再释放
		free(del);
	}

	ps->pSnoke = NULL;
	free(ps->pFood);
	//没有结束的食物也要释放掉
	
	ps->pFood = NULL;
	//食物和蛇身都要置NULL
}

void SnakeWalk(pSnoke ps)
//蛇的行走
{
	pSnakeNode pNext = (pSnakeNode)malloc(sizeof(SnakeNode));
	//创建个预先要前进的节点
	if (pNext==NULL)
	//判断是否创建成功
	{
		perror("SnakeWalk()::malloc()");
		return;
	}

	pNext->next = NULL;
	//先置NULL,判断是否下一个节点是食物
	switch (ps->Dir)
	{
	case UP:
		pNext->x = ps->pSnoke->x;
		pNext->y = ps->pSnoke->y-1;
		//向上走,横坐标不变,纵坐标减1
		break;
	case DOWN:
		pNext->x = ps->pSnoke->x;
		pNext->y = ps->pSnoke->y + 1;
		//向下走,横坐标不变,纵坐标加1

		break;
	case LEFT:
		pNext->x = ps->pSnoke->x - 2;
		//向左走,横坐标减去一个宽字符的长度.纵坐标不变
		pNext->y = ps->pSnoke->y;
		break;
	case RIGHT:
		pNext->x = ps->pSnoke->x + 2;
		//向右走,横坐标加上一个宽字符的长度.纵坐标不变

		pNext->y = ps->pSnoke->y;
		break;
	}

	NextIsFood(ps, pNext);
	//判断下一个是不是食物

	KillByWall(ps);
	//被墙撞死

	KillBySelf(ps);
	//咬到自己

}



void KillByWall(pSnoke ps)
//被墙撞死
{
	if (ps->pSnoke->x == 0 
		|| ps->pSnoke->x==56
		||ps->pSnoke->y==0
		||ps->pSnoke->y==26)
	//一旦头节点越级到墙边就说明撞墙了
	{
		ps->status = KILL_BY_WALL;
		//状态改变为撞墙了
	}

}

void KillBySelf(pSnoke ps)
//咬到自己
{
	pSnakeNode stu = ps->pSnoke->next;
	//先给个头节点的下一个节点
	while (stu)
	//开始遍历
	{
		if (stu->x == ps->pSnoke->x && stu->y == ps->pSnoke->y) 
		//一但头咬到身体的任何一个部位,就状态跳出
		{
			ps->status = KILL_BY_SELF;
			return;
		}

		stu = stu->next;
	}
}

void NextIsFood(pSnoke ps,pSnakeNode pNext)
//判断下一个是不是食物并打印
{
	if (pNext->x == ps->pFood->x && pNext->y == ps->pFood->y)
	//如果蛇的坐标和食物的坐标重合
	//就可以判断蛇吃到了食物
	{
		pNext->next = ps->pSnoke;
		//新的节点为蛇头连接蛇身
		ps->pSnoke = pNext;
		
		pSnakeNode tre = ps->pSnoke;
		
		while (tre)
		//循环打印过程
		{
			setpos(tre->x, tre->y);
			wprintf(L"%lc", BODY);
			tre = tre->next;
		}

		ps->Sorce += ps->FoodWeight;
		//计算上总分
		
		free(ps->pFood);
		//释放该食物节点
		CreatFood(ps);
		//创建新的食物
	}
	else
	{
		pNext->next = ps->pSnoke;
		ps->pSnoke = pNext;

		pSnakeNode tre = ps->pSnoke;
		//从头开始找.遇到最后一个节点的前一个节点就跳出

		while (tre->next->next )
		{

			setpos(tre->x, tre->y);
			wprintf(L"%lc", BODY);
			//一般遍历向下找
			// 一边打印身体
			tre = tre->next;
		}

		setpos(tre->x, tre->y);
		//遗留下最后一个节点
		wprintf(L"%lc", BODY);
		
		setpos(tre->next->x, tre->next->y);
		//最后一个节点打印空格
		printf("  ");

		free(tre->next);
		//最终要销毁蛇的最后一个节点
		tre->next = NULL;
		//形成移动的过程
	}
	
}


void Touchboard(pSnoke ps)
//按键识别
{
	do
	{
		setpos(70, 8);
		printf("每个食物的分数为:%02d  总分为:%3d", ps->FoodWeight, ps->Sorce);
		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))
		//感应ESC键
		{
			ps->status = ESC;
			//ESC键赋值
			
			break;
			//break可有可无
			//一旦状态不为OK就会跳出
		}
		else if (KEY_PRESS(VK_SPACE))
		//感应空格键
		{
			
			
			pause();
			//分装暂停函数
		}
		else if (KEY_PRESS(VK_F3))
		//感应F3
		{
			if (ps->SleepTime >= 80)
			//最快的休眠时间限制
			{
				ps->SleepTime -= 30;
				//时间减30
				ps->FoodWeight += 2;
				//分数加2
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			if (ps->FoodWeight > 2)
			{
				ps->SleepTime += 30;
				//时间加长
				ps->FoodWeight -= 2;
				//分数减2
			}
		}

		Sleep(ps->SleepTime);
		//每次调整蛇的休眠
		//休眠时间即蛇的速度

		SnakeWalk(ps);
		//蛇的行走
	} 
	while (ps->status == OK);
	//主要游戏运行过程在这里
	//以ps->status 的 OK 状态进行游戏的循环进行
	
}

void pause()
//分装空格暂停
{
	
	while (1)
	//无限循环
	{
		
		
		Sleep(100);
		//不断休眠
		
		if (KEY_PRESS(VK_SPACE))
		//一旦感应到就跳出休眠
			break;
	}

}

void printInfo(pSnoke ps)
//打印提示信息
{
	setpos(70, 15);
	printf("亲,请按 ↑ ↓  ←  → 来控制蛇的移动");
	
	setpos(70, 17);
	printf("不能穿墙,不能咬到自己");

	

	setpos(70, 19);
	printf("F3 为加速,F4 为减速");
	setpos(70, 21);
	printf("ESC 为退出,空格 为暂停");

	setpos(70, 23);
	printf("吃鱼专属版本");

	setpos(70, 25);
	printf("有更优化的大佬可以加我wx:zcy3075678575 欢迎指点在下");
}


void SnokeInit(pSnoke ps)
//蛇身的初始化与打印
{
	ps->pFood = ps->pSnoke = NULL;
	//食物和蛇头置为NULL
	
	ps->Sorce = 0;
	//得分清楚为0
	ps->FoodWeight = 10;
	//食物初始得分10

	ps->SleepTime = 200;
	//休眠时间200
	//决定着蛇的初始速度
	
	ps->Dir = RIGHT;
	//方向向右
	
	ps->status = OK;
	//状态正常

	pSnoke cur = NULL;
	int i = 0;

	for (i = 0; i < 5; i++)
	{
		pSnakeNode cat = (pSnakeNode)malloc(sizeof(SnakeNode));
		//创建蛇身
		if (cat == NULL)
		//检查是否创建成功
		{
			perror("SnokeInit()::malloc()");
			return;
		}

		cat->x = POS_X + 2 * i;
		//初始的长度一直往右走
		cat->y = POS_Y;
		cat->next=NULL;

		if (ps->pSnoke == NULL)
		//蛇头为NULL.就先放蛇头节点
		{
			ps->pSnoke = cat;
		}
		else
		//放完之后,蛇头下一个节点继续放
		{
			cat->next = ps->pSnoke;
			ps->pSnoke = cat;
		}
	}

	pSnakeNode stu = ps->pSnoke;
	while (stu)
	//最终打印
	{
		setpos(stu->x, stu->y);
		wprintf(L"%lc", BODY);
		stu = stu->next;
	}
	
}


void CreatFood(pSnoke ps)
//食物的初始化与打印
{
	int x = 0;
	int y = 0;
	//初始化预定值

again:
	do
	{
		x = rand() % 53 + 2;
		y = rand() % 24 + 1;
	} while (x%2!=0);
	//跳出的是偶数就是我们想要的值

	pSnakeNode stu = ps->pSnoke;
	//给个头来遍历链表


	while (stu)
	//遍历我们每一个蛇身的坐标
	{
		if (stu->x == x && stu->y == y)
		//在遍历蛇身坐标的过程中
		//一旦蛇身坐标等于该食物坐标就goto跳回
		{
			goto again;
			//跳到上面的do...while循环
		}

		stu = stu->next;

	}

	pSnakeNode cur = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (cur == NULL)
	{
		perror("CreatFood()::malloc()");
		return;
	}

	cur->x = x;
	cur->y = y;
	ps->pFood = cur;
	//坐标附上
	//食物的节点同时也接上

	setpos(cur->x, cur->y);
	wprintf(L"%lc", FOOD);
	//给定随机生成的坐标并打印
	
}

void setpos(int x, int y)
//设置光标位置
{
	HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
	//得到句手

	COORD pos = { x,y };
	//输入想要的坐标
	
	SetConsoleCursorPosition(handle, pos);
	//执行所在的坐标
}


void TrandCUrsor()
//隐藏光标
{
	//去除光标
	HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
	//得到句手

	CONSOLE_CURSOR_INFO CursorInfo;
	//设置光标结构体变量

	GetConsoleCursorInfo(handle, &CursorInfo);
	//得到光标信息

	CursorInfo.bVisible = false;
	//隐藏光标

	SetConsoleCursorInfo(handle, &CursorInfo);
	//操作光标


}


void setwelcome()
//打印欢迎界面
{
	setpos(46, 15);

	printf("各位王子 ,公主殿下");

	setpos(46, 17);

	printf("欢迎进入贪吃蛇小游戏!");

	setpos(49, 30);
	system("pause");
	system("cls");

	setpos(40, 15);
	printf("亲,请按 ↑ ↓  ←  → 来控制蛇的移动");
	setpos(40, 17);
	printf("F3为加速,F4为减速");
	setpos(40, 19);
	printf("加速可以得到更高分");
	setpos(49, 30);
	system("pause");
	system("cls");

}


void Printmap()
//打印地图
{

	//上
	setpos(0, 0);
	//先定位第一行第一列

	int i = 0;
	for ( i = 0; i <=56; i+=2)
	//上方墙两个字符空间为一个宽字符字符
	{
		wprintf(L"%lc", WALL);
		//打印墙
	}

	//下
	setpos(0, 26);
	//最后一行即为下
	for ( i = 0; i <= 56; i += 2)
	//下方墙两个字符空间为一个宽字符字符
	{
		wprintf(L"%lc", WALL);
	}


	//左
	for ( i = 0; i <=25; i++)
	{
		setpos(0, i);
		//每次打印就要移动到下一行
		wprintf(L"%lc", WALL);
	}

	//右
	for (i = 0; i <= 25; i++)
	{
		setpos(56, i);
		wprintf(L"%lc", WALL);
	}

	setpos(49, 30);
	
}

贪吃蛇 展示视频

鱼式疯言

我们要实现我们的贪吃蛇

小编认为还是要抓住追重要的 数据结构 里面的 单向不带头不循环链表 的底层实现才能完美的实现我们想要的贪吃蛇的效果。

❤️❤️❤️❤️

总结

友友们,别着急走啊,一起来梳理下本篇延迟蛇内容吧
在本篇内我们主要讲解了
一. 贪吃蛇游戏功能介绍:初步介绍了我们贪吃蛇游戏功能的各个功能
二. 游戏环境介绍:说明了我们游戏环境的需要设置光标和页面的主题的打印
三. 游戏设计和分析过程:我们整体框架分为开始,中间,结束 三个过程
四. 核心逻辑实现分析:小编带着大家一步一步分解我们贪吃蛇的每个功能函数如何去封装,然后去调用实现我们相要的功能的
五. 参考代码:展示了我们最终所有的源代码。

如果觉得小编写的还不错的咱可支持三关下,不妥当的咱评论区指正

希望我的文章能给各位家人们带来哪怕一点点的收获就是小编创作的最大动力
在这里插入图片描述

你可能感兴趣的:(#,一川游戏落盛夏,c语言,开发语言)