贪吃蛇大作战(C语言--实战项目)

朋友们!好久不见。经过一段时间的沉淀,我这篇文章来和大家分享贪吃蛇大作战这个游戏是怎么实现的。

(一).贪吃蛇背景了解及效果展示

首先相信贪吃蛇游戏绝对称的上是我们00后的童年,不仅是贪吃蛇还有俄罗斯⽅块,扫雷等都是以前十分流行的游戏,下面我们就通过代码的形式进行贪吃蛇的实现,在进行代码实现之前我们先来看看实现的效果看看有些什么,相信大家一定会对它十分感兴趣的。

贪吃蛇游戏

上面这个就是关于贪吃蛇游戏的实现效果图,相信大家都十分的想了解到底它是怎样实现的,那么下面我们就来学习关于它的实现。

(二).预了解知识(相关Win32API介绍)

首先在实现贪吃蛇之前我们要先了解一些关于Win32API的知识。

2.1.控制台程序

在这里我们需要改变控制台,才能完成贪吃蛇游戏,如图:

这里我们首先要将默认终端应用程序改成如图所示,

mode命令:指的是改变控制台界面大小

如图所示:

title命令:改变控制台的名字

总的来说这两个命令实际运用如图所示:

改变的就是如图中的两个东西实际代码如下:

#include<stdio.h>
int main()
{system("mode con cols=100 lines=30");system("title 贪吃蛇");return 0;
}

 2.2.控制台屏幕上的坐标COORD

COORD也就是控制台屏幕上的坐标

也就是如图所示的坐标。

COORD类型的声明:

typedef struct _COORD {SHORT X;SHORT Y;
} COORD, *PCOORD;

 给坐标赋值:

COORD pos = { 10, 15 };

2.3.GetStdHandle

GetStdHandle是⼀个Windows API函数。它⽤于从⼀个特定的标准设备(标准输⼊、标准输出或标 准错误)中取得⼀个句柄(⽤来标识不同设备的数值),使⽤这个句柄可以操作设备。

这里简而言之就是和我们鼠标相同的东西。

HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

 这里运用GetStdHandle来获取句柄。

2.4.GetConsoleCursorInfo 函数

作用:检索有关指定控制台屏幕缓冲区的光标⼤⼩和可⻅性的信息

HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息

 GetConsoleCursorInfo 函数的实际运用就是如图所示,用来获取控制台光标的信息。

2.5.CONSOLE_CURSOR_INFO

这个结构体,包含有关控制台光标的信息

typedef struct _CONSOLE_CURSOR_INFO {DWORD dwSize;BOOL bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;

 dwSize:由光标填充的字符单元格的百分⽐。 此值介于1到100之间。 光标外观会变化,范围从完 全填充单元格到单元底部的⽔平线条。

 bVisible:游标的可⻅性。 如果光标可⻅,则此成员为 TRUE。

CursorInfo.bVisible = false; //隐藏控制台光标

2.6.SetConsoleCursorInfo 

作用:设置指定控制台屏幕缓冲区的光标的⼤⼩和可⻅性。

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

这里就是用 SetConsoleCursorInfo函数来设置控制台光标状态, 只有运用了这个函数光标才能够被隐藏。

2.7.SetConsoleCursorPosition

作用:设置指定控制台屏幕缓冲区中的光标位置,我们将想要设置的坐标信息放在COORD类型的pos中,调 ⽤SetConsoleCursorPosition函数将光标位置设置到指定的位置。简而言之就是将光标设置在自己想要它在的地方通过坐标的形式。

COORD pos = { 10, 5};HANDLE hOutput = NULL;//获取标准输出的句柄(⽤来标识不同设备的数值)hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//设置标准输出上光标的位置为posSetConsoleCursorPosition(hOutput, pos);

 SetPos:封装⼀个设置光标位置的函数

//设置光标的坐标
void SetPos(short x, short y)
{COORD pos = { x, y };HANDLE hOutput = NULL;//获取标准输出的句柄(⽤来标识不同设备的数值)hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//设置标准输出上光标的位置为posSetConsoleCursorPosition(hOutput, pos);
}

 2.8GetAsyncKeyState

获取按键情况,GetAsyncKeyState的函数原型如下:

SHORT GetAsyncKeyState(int vKey
);

 实例:检测数字键

#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )
#include <stdio.h>
#include <windows.h>
int main()
{ while (1){if (KEY_PRESS(0x30)){printf("0\n");}else if (KEY_PRESS(0x31)){printf("1\n");}else if (KEY_PRESS(0x32)){printf("2\n");}else if (KEY_PRESS(0x33)){printf("3\n");}else if (KEY_PRESS(0x34)){printf("4\n");}else if (KEY_PRESS(0x35)){printf("5\n");}else if (KEY_PRESS(0x36))
{printf("6\n");}else if (KEY_PRESS(0x37)){printf("7\n");}else if (KEY_PRESS(0x38)){printf("8\n");}else if (KEY_PRESS(0x39)){printf("9\n");}}return 0;
}

这里就是用来检测键盘上面的键是否被按过。

2.9设置本地化环境

<locale.h>本地化

提供的函数⽤于控制C标准库中对于不同的地区会产⽣不⼀样⾏为的部分。 在标准中,依赖地区的部分有以下⼏项:

• 数字量的格式

• 货币量的格式

• 字符集

• ⽇期和时间的表⽰形式

2.10.setlocale

C标准给第⼆个参数仅定义了2种可能取值:"C"(正常模式)和" "(本地模式)。

在我们实现的时候通常我们会先把它设置为本地模式,以便进行环境的匹配。

setlocale(LC_ALL, "C"); 注意这里使用setlocale函数要有头文件<locale.h>。

2.11.宽字符的打印

那如果想在屏幕上打印宽字符,怎么打印呢?

宽字符的字⾯量必须加上前缀“L”,否则 C 语⾔会把字⾯量当作窄字符类型处理。前缀“L”在单引 号前⾯,表⽰宽字符,对应 wprintf() 的占位符为 %lc ;在双引号前⾯,表⽰宽字符串,对应 wprintf() 的占位符为 %ls 。

这三个内容其实是连在一起用的,要先进行本地环境的设置再来进行宽字符的打印。

总结:上面的全部都是我们在完成贪吃蛇之前需要了解的知识,可能我们就这样直接来看可能不容易理解下面我们进行贪吃蛇的实现将这些知识带进去我们大家可能更容易理解。 

(三).项目实践

 项目进行之前我们要先进行准备工作,首先定义一个结构体,来包装贪吃蛇

typedef struct Snake
{pSnakeNode _psnake;//指向蛇头的指针pSnakeNode _pfood;//指向食物节点的指针enum DIRECTION _dir;//蛇的方向enum GAME_STATUS _status;//游戏的状态int _food_weight;//一个食物的分数int _score;//总分数int _sleep_time;//休息时间,休息时间越短,速度越快
}Snake,* pSnake;

这里面就是贪吃蛇的全部东西我们这样将它的七个内容定义成一个结构体的好处是有利于后期的管理和实现,同样里面蛇的方向有多个,游戏状态也有多种,这两个我们就可以利用枚举的方式来包装起来,蛇的节点类型其实就是一个链表同样这里我们也需要定义一个结构体来表示蛇的节点,这三个实现的代码我总结到下面:

//枚举蛇的方向
enum DIRECTION
{UP,DOWN,LEFT,RIGHT
};
//枚举游戏的状态
enum GAME_STATUS {OK,//正常KILL_BY_WALL,//撞墙KILL_BY_SELF,//撞到自己END_NORMAL //正常退出
};//蛇身节点类型
typedef struct SnakeNode
{int x;int y;struct SnakeNode* next;}SnakeNode,* pSnakeNode;

好了当我们把这些准备工作做好以后我们就可以来进行贪吃蛇的实现了,首先我们来看游戏开始部分。

3.1.游戏开始(GameStart)

游戏开始阶段一共有七个内容我们来逐一学习。 

3.1.1.设置游戏窗⼝的⼤⼩

设置游戏窗口的大小,就是我们上面说的mode命令 

system("mode con cols=100 lines=30");就是有system函数来设置控制台窗口的大小。

3.1.2.设置窗⼝的名字

设置窗口的名字就是通过title命令来设置窗口的名字

 system("title 贪吃蛇");就是运用system函数来设置窗口的名字

当这两个程序完成之后的窗口样子就是如下:

3.1.3.隐藏屏幕光标

隐藏光标实际上就是把如图所示的东西给隐藏掉

 那么这个程序我们该如何去写呢?接下来紧跟我们脚步我们一起来学习

通过上面我们了解的一些知识我们先看看下面这段代码:


//设置光标的坐标
void SetPos(short x, short y)
{HANDLE hOutput = NULL;//获取标准输出的句柄(⽤来标识不同设备的数值)hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//设置标准输出上光标的位置为posCOORD pos = { x, y };SetConsoleCursorPosition(hOutput, pos);
}//获取输出设备的句柄
HANDLE hOutput = NULL;
hOutput=GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo = {0};//定义一个光标信息的结构体
GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
CursorInfo.bVisible = false;//隐藏控制台光标  
SetConsoleCursorInfo(hOutput, &CursorInfo);//设置光标信息

 这里面就解释了我们如何来隐藏光标,先获取句柄,定义一个houtput来接收,再通过定义一个结构体,在获取控制台光标信息,最后再来隐藏控制台光标。

3.1.4.打印欢迎界⾯-WelcomeToGame

 

欢迎界面就是如图所示,同样我们的代码程序如下:

void WelcomeToGame()
{SetPos(38,14);wprintf(L"欢迎来到贪吃蛇小游戏");SetPos(40, 18);system("pause");system("cls");//清理界面SetPos(20, 14);wprintf(L"用↑ . ↓ . ← . →来控制蛇的移动,按F3加速,按F4减速\n");SetPos(20, 15);wprintf(L"加速能够获得更高的分数\n");SetPos(40, 20);system("pause");system("cls");
}

 

3.1.5.创建地图-CreateMap

地图创建我们需要我们来进行创建以下的界面

 

 

创建一个x轴为58y轴为26的一个游戏地图,这里我们就可以运用循环语句来打印□从而创建游戏窗口。

实际代码如图所示:

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

上面的SetPos是指的是定位光标的位置。

3.1.6.初始化蛇⾝-InitSnake

//创建蛇身
void InitSnack(pSnake ps)
{int i = 0;pSnakeNode cur = NULL;for (i; i < 5; i++){cur = (pSnakeNode)malloc(sizeof(SnakeNode));if (cur == NULL){perror("InitSnack()::malloc");return;}cur->next = NULL;cur->x = POS_X + 2 * i;cur->y = POS_Y;//采用头插法if (ps->_psnake==NULL){ps->_psnake = cur;}else{cur->next = ps->_psnake;ps->_psnake = cur;}}cur = ps->_psnake;while (cur){SetPos(cur->x, cur->y);wprintf(L"%lc", BODY);cur = cur->next;}//设置贪吃蛇属性ps->_dir = RIGHT;//默认向右ps->_score = 0;ps->_food_weight = 10;ps->_sleep_time = 200;//单位是毫秒ps->_status = OK;}

3.1.7.创建⻝物

//创建食物
void CreatFood(pSnake ps)
{int x = 0;int y = 0;//生成x必须是2的倍数//x:2--54//y:1--25
again:do{x = rand() % 53 + 2;y = rand() % 25 + 1;} while (x % 2 != 0);//x和y不能与蛇身体的位置冲突pSnakeNode cur = ps->_psnake;while (cur){if (x == cur->x && y == cur->y){goto again;//如果食物与蛇身冲突的话则再来一遍}cur = cur->next;}//创建食物的节点pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));if (pFood == NULL){perror("pFood::malloc");return;}pFood->x = x;pFood->y = y;pFood->next = NULL;SetPos(x, y);wprintf(L"%lc", FOOD);ps->_pfood = pFood;
}

3.2.游戏进行(GameRun)

3.2.1.右侧打印帮助信息-PrintHelpInfo

//打印运行游戏旁边的提示信息
void printfhelpInfo()
{SetPos(64, 12);wprintf(L"%ls", L"不能撞墙,不能咬到自己");SetPos(64, 13);wprintf(L"%ls", L"用↑ . ↓ . ← . →来控制蛇的移动,");SetPos(64, 14);wprintf(L"%ls", L"按F3加速,按F4减速");SetPos(64, 15);wprintf(L"%ls", L"按ESC退出游戏,按空格暂停游戏");}

3.2.2.打印当前已获得分数和每个⻝物的分数 

//打印总分数和食物的分值SetPos(64, 9);printf("总分数:%d\n", ps->_score);SetPos(64, 10);printf("当前食物的分数:%2d\n", ps->_food_weight);

3.2.3.获取按键情况-KEY_PRESS

//按键的设置
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_NORMAL;
}
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;}
}

3.2.4.根据按键情况移动蛇-SnakeMove 2~4循环,直到游戏是结束状态

void SnakeMove(pSnake ps)
{//创建一个节点表示蛇即将要到的下一个节点pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));if (pNextNode == NULL){perror("pNextNode()::malloc!");return;}switch (ps->_dir){case UP:pNextNode->x = ps->_psnake->x;pNextNode->y = ps->_psnake->y-1;break;case DOWN:pNextNode->x = ps->_psnake->x;pNextNode->y = ps->_psnake->y + 1;break;case LEFT:pNextNode->x = ps->_psnake->x-2;pNextNode->y = ps->_psnake->y;break;case RIGHT:pNextNode->x = ps->_psnake->x+2;pNextNode->y = ps->_psnake->y;break;}//检测下一个坐标是否是食物if (NextIsFood(pNextNode,ps)){EatFood(pNextNode, ps);}else{NoFood(pNextNode, ps);}//检测是否撞墙KillByWall(ps);//检测是否撞到自己KillBySelf(ps);}
3.2.4.1. 根据蛇头的坐标和⽅向,计算下⼀个节点的坐标
void SnakeMove(pSnake ps)
{//创建一个节点表示蛇即将要到的下一个节点pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));if (pNextNode == NULL){perror("pNextNode()::malloc!");return;}switch (ps->_dir){case UP:pNextNode->x = ps->_psnake->x;pNextNode->y = ps->_psnake->y-1;break;case DOWN:pNextNode->x = ps->_psnake->x;pNextNode->y = ps->_psnake->y + 1;break;case LEFT:pNextNode->x = ps->_psnake->x-2;pNextNode->y = ps->_psnake->y;break;case RIGHT:pNextNode->x = ps->_psnake->x+2;pNextNode->y = ps->_psnake->y;break;}//检测下一个坐标是否是食物if (NextIsFood(pNextNode,ps)){EatFood(pNextNode, ps);}else{NoFood(pNextNode, ps);}//检测是否撞墙KillByWall(ps);//检测是否撞到自己KillBySelf(ps);}
3.2.4.2. 判断下⼀个节点是否是⻝物-NextIsFood
int NextIsFood(pSnakeNode pn, pSnake ps)
{return (ps->_pfood->x == pn->x && ps->_pfood->y == pn->y);
}
3.2.4.3.是⻝物就吃掉-EatFood
void EatFood(pSnakeNode pn, pSnake ps)
{//采用头插法将食物放上去ps->_pfood->next = ps->_psnake;ps->_psnake = ps->_pfood;free(pn);pn = NULL;pSnakeNode cur = ps->_psnake;//打印while (cur){SetPos(cur->x,cur->y);wprintf(L"%lc", BODY);cur = cur->next;}ps->_score += ps->_food_weight;//重新创建食物CreatFood(ps);
}
3.2.4.4.不是⻝物,吃掉⻝物,尾巴删除⼀节-NoFood
void NoFood(pSnakeNode pn, pSnake ps)
{pn->next = ps->_psnake;ps->_psnake = pn;pSnakeNode cur = ps->_psnake;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("  ");free(cur->next);cur->next = NULL;
}
3.2.4.5.判断是否撞墙-KillByWall
//检测是否撞墙
void KillByWall(pSnake ps)
{if (ps->_psnake->x == 0 || ps->_psnake->x == 56 || ps->_psnake->y == 0 || ps->_psnake->y == 26){ps->_status = KILL_BY_WALL;}
}
3.2.4.6.判断是否撞上⾃⼰-KillBySelf
//检测是否撞到自己
void KillBySelf(pSnake ps)
{pSnakeNode cur = ps->_psnake->next;while (cur){if (cur->x == ps->_psnake->x && cur->y == ps->_psnake->y){ps->_status = KILL_BY_SELF;}cur = cur->next;}
}

3.3游戏结束(GameEnd)

3.3.1.告知游戏结束的原因

3.3.2.释放蛇⾝节点

//结束游戏--善后工作
void GameEnd(pSnake ps)
{switch (ps->_status){case END_NORMAL:printf("主动结束游戏\n");break;case KILL_BY_SELF:printf("撞到了自己,游戏结束\n");break;case KILL_BY_WALL:printf("撞到了墙游戏结束\n");break;}//释放链表pSnakeNode cur = ps->_psnake;while (cur){pSnakeNode del = cur;cur = cur->next;free(del);}
}

(四).代码汇总

4.1.snake.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<locale.h>
#include<windows.h>
#include<stdbool.h>
#include<stdlib.h>
#include<time.h>#define POS_X 24
#define POS_Y 5#define WALL L'□'
#define BODY L'●'
#define FOOD L'★'#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)
//枚举蛇的方向
enum DIRECTION
{UP,DOWN,LEFT,RIGHT
};
//枚举游戏的状态
enum GAME_STATUS {OK,//正常KILL_BY_WALL,//撞墙KILL_BY_SELF,//撞到自己END_NORMAL //正常退出
};//蛇身节点类型
typedef struct SnakeNode
{int x;int y;struct SnakeNode* next;}SnakeNode,* pSnakeNode;//贪吃蛇
typedef struct Snake
{pSnakeNode _psnake;//指向蛇头的指针pSnakeNode _pfood;//指向食物节点的指针enum DIRECTION _dir;//蛇的方向enum GAME_STATUS _status;//游戏的状态int _food_weight;//一个食物的分数int _score;//总分数int _sleep_time;//休息时间,休息时间越短,速度越快
}Snake,* pSnake;
//定位光标位置
void SetPos(short x, short y);//初始化游戏
void GameStart(pSnake ps);//打印欢迎界面和功能介绍
void WelcomeToGame(pSnake ps);//2.绘制地图
void CreatMap();//初始化蛇
void InitSnack(pSnake ps);//创建食物
void CreatFood(pSnake ps);//运行游戏
void GameRun(pSnake ps);//蛇的移动
void SnakeMove(pSnake ps);//实现暂停逻辑
void pause();//检测下一个坐标是否是食物
int NextIsFood(pSnakeNode pn, pSnake ps);//下一个位置是食物吃掉食物
void EatFood(pSnakeNode pn, pSnake ps);//如果下一个坐标不是食物
void NoFood(pSnakeNode pn, pSnake ps);//检测是否撞墙
void KillByWall(pSnake ps);//检测是否撞到自己
void KillBySelf(pSnake ps);//结束游戏--善后工作
void GameEnd(pSnake ps);

4.2.snake.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"snake.h"//设置光标的坐标
void SetPos(short x, short y)
{HANDLE hOutput = NULL;//获取标准输出的句柄(⽤来标识不同设备的数值)hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//设置标准输出上光标的位置为posCOORD pos = { x, y };SetConsoleCursorPosition(hOutput, pos);
}
//1.打印欢迎界面和功能介绍
void WelcomeToGame()
{SetPos(38,14);wprintf(L"欢迎来到贪吃蛇小游戏");SetPos(40, 18);system("pause");system("cls");//清理界面SetPos(20, 14);wprintf(L"用↑ . ↓ . ← . →来控制蛇的移动,按F3加速,按F4减速\n");SetPos(20, 15);wprintf(L"加速能够获得更高的分数\n");SetPos(40, 20);system("pause");system("cls");
}//2.绘制地图
void CreatMap()
{//上int i = 0;for (i = 0; i < 29; i++){wprintf(L"%lc", WALL);}//下SetPos(0, 26);for (i = 0; i < 29; i++){wprintf(L"%lc", WALL);}//左for (i = 1; i < 26; i++){SetPos(0, i);wprintf(L"%lc", WALL);}//右for (i = 1; i < 26; i++){SetPos(56, i);wprintf(L"%lc", WALL);}}//创建蛇身
void InitSnack(pSnake ps)
{int i = 0;pSnakeNode cur = NULL;for (i; i < 5; i++){cur = (pSnakeNode)malloc(sizeof(SnakeNode));if (cur == NULL){perror("InitSnack()::malloc");return;}cur->next = NULL;cur->x = POS_X + 2 * i;cur->y = POS_Y;//采用头插法if (ps->_psnake==NULL){ps->_psnake = cur;}else{cur->next = ps->_psnake;ps->_psnake = cur;}}cur = ps->_psnake;while (cur){SetPos(cur->x, cur->y);wprintf(L"%lc", BODY);cur = cur->next;}//设置贪吃蛇属性ps->_dir = RIGHT;//默认向右ps->_score = 0;ps->_food_weight = 10;ps->_sleep_time = 200;//单位是毫秒ps->_status = OK;}//创建食物
void CreatFood(pSnake ps)
{int x = 0;int y = 0;//生成x必须是2的倍数//x:2--54//y:1--25
again:do{x = rand() % 53 + 2;y = rand() % 25 + 1;} while (x % 2 != 0);//x和y不能与蛇身体的位置冲突pSnakeNode cur = ps->_psnake;while (cur){if (x == cur->x && y == cur->y){goto again;//如果食物与蛇身冲突的话则再来一遍}cur = cur->next;}//创建食物的节点pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode));if (pFood == NULL){perror("pFood::malloc");return;}pFood->x = x;pFood->y = y;pFood->next = NULL;SetPos(x, y);wprintf(L"%lc", FOOD);ps->_pfood = pFood;
}void GameStart(pSnake ps)
{//首先设置窗口的大小,进行光标隐藏system("mode con cols=100 lines=30");system("title 贪吃蛇");//获取输出设备的句柄HANDLE hOutput = NULL;hOutput=GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_CURSOR_INFO CursorInfo = {0};//定义一个光标信息的结构体GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息CursorInfo.bVisible = false;//隐藏控制台光标  SetConsoleCursorInfo(hOutput, &CursorInfo);//设置光标信息//1.打印欢迎界面和功能介绍WelcomeToGame();//2.绘制地图CreatMap();//3.创建蛇InitSnack(ps);//4.创建食物CreatFood(ps);
}//打印运行游戏旁边的提示信息
void printfhelpInfo()
{SetPos(64, 12);wprintf(L"%ls", L"不能撞墙,不能咬到自己");SetPos(64, 13);wprintf(L"%ls", L"用↑ . ↓ . ← . →来控制蛇的移动,");SetPos(64, 14);wprintf(L"%ls", L"按F3加速,按F4减速");SetPos(64, 15);wprintf(L"%ls", L"按ESC退出游戏,按空格暂停游戏");}void pause()
{while (1){Sleep(200);if (KEY_PRESS(VK_SPACE)){break;}}
}int NextIsFood(pSnakeNode pn, pSnake ps)
{return (ps->_pfood->x == pn->x && ps->_pfood->y == pn->y);
}void EatFood(pSnakeNode pn, pSnake ps)
{//采用头插法将食物放上去ps->_pfood->next = ps->_psnake;ps->_psnake = ps->_pfood;free(pn);pn = NULL;pSnakeNode cur = ps->_psnake;//打印while (cur){SetPos(cur->x,cur->y);wprintf(L"%lc", BODY);cur = cur->next;}ps->_score += ps->_food_weight;//重新创建食物CreatFood(ps);
}void NoFood(pSnakeNode pn, pSnake ps)
{pn->next = ps->_psnake;ps->_psnake = pn;pSnakeNode cur = ps->_psnake;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("  ");free(cur->next);cur->next = NULL;
}//检测是否撞墙
void KillByWall(pSnake ps)
{if (ps->_psnake->x == 0 || ps->_psnake->x == 56 || ps->_psnake->y == 0 || ps->_psnake->y == 26){ps->_status = KILL_BY_WALL;}
}//检测是否撞到自己
void KillBySelf(pSnake ps)
{pSnakeNode cur = ps->_psnake->next;while (cur){if (cur->x == ps->_psnake->x && cur->y == ps->_psnake->y){ps->_status = KILL_BY_SELF;}cur = cur->next;}
}
void SnakeMove(pSnake ps)
{//创建一个节点表示蛇即将要到的下一个节点pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));if (pNextNode == NULL){perror("pNextNode()::malloc!");return;}switch (ps->_dir){case UP:pNextNode->x = ps->_psnake->x;pNextNode->y = ps->_psnake->y-1;break;case DOWN:pNextNode->x = ps->_psnake->x;pNextNode->y = ps->_psnake->y + 1;break;case LEFT:pNextNode->x = ps->_psnake->x-2;pNextNode->y = ps->_psnake->y;break;case RIGHT:pNextNode->x = ps->_psnake->x+2;pNextNode->y = ps->_psnake->y;break;}//检测下一个坐标是否是食物if (NextIsFood(pNextNode,ps)){EatFood(pNextNode, ps);}else{NoFood(pNextNode, ps);}//检测是否撞墙KillByWall(ps);//检测是否撞到自己KillBySelf(ps);}//运行游戏
void GameRun(pSnake ps)
{printfhelpInfo();do {//打印总分数和食物的分值SetPos(64, 9);printf("总分数:%d\n", ps->_score);SetPos(64, 10);printf("当前食物的分数:%2d\n", 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_NORMAL;}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;}}SnakeMove(ps);Sleep(ps->_sleep_time);} while (ps->_status==OK);
}//结束游戏--善后工作
void GameEnd(pSnake ps)
{switch (ps->_status){case END_NORMAL:printf("主动结束游戏\n");break;case KILL_BY_SELF:printf("撞到了自己,游戏结束\n");break;case KILL_BY_WALL:printf("撞到了墙游戏结束\n");break;}//释放链表pSnakeNode cur = ps->_psnake;while (cur){pSnakeNode del = cur;cur = cur->next;free(del);}
}

4.3.test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"snake.h"//完成游戏的测试逻辑
void test()
{int ch = 0;do {system("cls");//在每次开始前先清理屏幕//创建贪吃蛇Snake snake = { 0 };//初始化游戏//1.打印欢迎界面//2.功能介绍//3.绘制地图//4.创建蛇//5.创建食物//6.设置游戏相关信息GameStart(&snake);//运行游戏GameRun(&snake);//结束游戏--善后工作GameEnd(&snake);SetPos(20,15);printf("只来一局?(Y/N):");ch=getchar();getchar();while (getchar() != '\n');} while (ch == 'Y' || ch == 'y');SetPos(0, 27);
}int main()
{//设置适配环境(本地环境)setlocale(LC_ALL, "");srand((unsigned int)time(NULL));//生成随机数值test();return 0;
}

好了,兄弟们以上就是贪吃蛇项目的全部内容,通过去写这个项目我自己反正是感悟挺大的,因为从学习到自己实现这个过程虽然很艰苦,但当自己写出来能实现的时候觉得一切都值得,以后的编程之路一定会使我越来越牛掰,一起加油,兄弟们!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据库表的约束】

文章目录 一、NULL vs &#xff08;空字符串&#xff09;二、not null 和default三、列描述字段comment四、zerofill五、primary key 主键总结 一、NULL vs ‘’&#xff08;空字符串&#xff09; NULL和空字符串’’ NULL代表什么都没有。 空字符串’代表有&#xff0c;但串…

纯干货分享|源代码泄露的有效方法

企业的源代码怎么加密&#xff1f; 源代码防泄密的重点和方法到底是怎样的&#xff1f; 源代码开发环境复杂&#xff0c;涉及的开发软件、文件类型庞杂多变&#xff0c;究竟有什么源代码加密软件能够适应众多开发软件而不影响原有的工作效率&#xff1f; 相信这是很多IT管理…

如何用TONGYILingma进行AI辅助编程?

通义灵码&#xff0c;是阿里云出品的一款基于通义大模型的智能编码辅助工具&#xff0c;提供行级/函数级实时续写、自然语言生成代码、单元测试生成、代码优化、注释生成、代码解释、研发智能问答、异常报错排查等能力&#xff0c;并针对阿里云的云服务使用场景调优&#xff0c…

面试笔记——工厂模式(简单工厂、工厂方法模式、抽象工厂模式)

场景需求&#xff1a;设计一个咖啡店点餐系统。 设计一个咖啡类&#xff08;Coffee&#xff09;&#xff0c;并定义其两个子类&#xff08;美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】&#xff09;&#xff1b;再设计一个咖啡店类&#xff08;CoffeeStore&#xff09…

软件设计师-应用技术-UML建模题3

基础知识及技巧&#xff1a; 1. 用例图&#xff1a; 1.1 考点&#xff1a; 题干里面有关项目的详细描述&#xff0c;完整用例图中的某些参与者和某些用来扣掉&#xff0c;根据题干内容和已有用例图补充。根据题干&#xff0c;分析用例图之间的关系。 1.2 基础知识&#xff…

Linux进程通信-信号

信号概念 信号是 Linux 进程间通信的最古老的方式之一&#xff0c;是事件发生时对进程的通知机制&#xff0c;有时也称之为软件中断&#xff0c;它是在软件层次上对中断机制的一种模拟&#xff0c;是一种异步通信的方式。信号 可以导致一个正在运行的进程被另一个正在运行的异…

通过 Java 操作 redis -- String 基本命令

关于 redis String 类型的相关命令推荐看 Redis - String 字符串 要想通过 Java 操作 redis&#xff0c;首先要连接上 redis 服务器&#xff0c;推荐看通过 Java 操作 redis -- 连接 redis 本博客只介绍了一小部分常用的命令&#xff0c;其他的命令根据上面推荐的博客也能很简单…

Day 63:单调栈 LeedCode 84.柱状图中最大的矩形

84. 柱状图中最大的矩形 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 示例 1: 输入&#xff1a;heights [2,1,5,6,2,3] 输出&#xff1a;10 解释&a…

论文精读-基于FPGA的卷积神经网络和视觉Transformer通用加速器

文章目录 论文精读-基于FPGA的卷积神经网络和视觉Transformer通用加速器概述研究背景卷积和注意力机制概述计算方式差异非线性与归一化操作差异 问题小结 加速器设计乘法单元探索非线性与归一化加速单元加速器架构 实验结果QA 论文精读-基于FPGA的卷积神经网络和视觉Transforme…

交易复盘-20240507

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票 10%的时候可以操作&#xff0c; 90%的时间适合空仓等待 蔚蓝生物 (5)|[9:25]|[36187万]|4.86 百合花…

从0到1提审苹果商店(appstore)上线一款新APP

本篇主要复盘和介绍一款APP如何从0到1上线到苹果商店,将我自己项目遇到的坑跟大家分享,希望能为同样做开发或者运营的你提供经验,少走弯路。 如果你是24年1月1日之后开始首次提审APP,还需要先将自己的APP在工信部备案,苹果后台增加了工信部备案号的填写,备案方法和经验如…

SAP PP学习笔记12 - 评估MRP的运行结果

上一章讲了MRP的概念&#xff0c;参数&#xff0c;配置等内容。 SAP PP学习笔记11 - PP中的MRP相关概念&#xff0c;参数&#xff0c;配置-CSDN博客 本章来讲 MRP跑完之后呢&#xff0c;要怎么评估这个MRP的运行结果。 1&#xff0c;Stock/Requirements List and MRP List 在…

T型槽地轨承载力是如何连接整个制造过程的强力桥梁(北重公司设计)

T型槽地轨承载力的定义和计算 T型槽地轨是一种用于工业设备运输和装配的关键组件。它由世界上各行各业的生产商广泛采用&#xff0c;其有效的承载力使其成为连接整个制造过程的强力桥梁。本文将介绍T型槽地轨的承载力以及相关的设计要点和应用。 承载力的定义和计算 承载力是…

某制造公司屋顶分布式光伏发电案例分享--分布式光伏电力监控系统解决方案

安科瑞薛瑶瑶18701709087/17343930412 ★分布式光伏监控系统 分布式光伏监控电力系统遵循安全可靠、经济合理原则&#xff0c;满足电力系统自动化总体规划要求&#xff0c;且充分考虑光伏发电的因素&#xff0c;对分布式光伏发电、用电进行集中监控、统一调度、统一运维、满足…

vivado Zynq UltraScale+ MPSoC 比特流设置

Zynq UltraScale MPSoC 比特流设置 下表所示 Zynq UltraScale MPSoC 器件的器件配置设置可搭配 set_property <Setting> <Value> [current_design] Vivado 工具 Tcl 命令一起使用。

本地运行AI大模型简单示例

一、引言 大模型LLM英文全称是Large Language Model&#xff0c;是指包含超大规模参数&#xff08;通常在十亿个以上&#xff09;的神经网络模型。2022年11月底&#xff0c;人工智能对话聊天机器人ChatGPT一经推出&#xff0c;人们利用ChatGPT这样的大模型帮助解决很多事情&am…

【Qt 学习笔记】Qt常用控件 | 输入类控件 | Date/Time Edit的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 输入类控件 | Spin Box的使用及说明 文章编号&#xff1…

YOLOv5 V7.0 - rknn模型的验证 输出精度(P)、召回率(R)、mAP50、mAP50-95

1.简介 RKNN官方没有提供YOLOv5模型的验证工具&#xff0c;而YOLOv5自带的验证工具只能验证pytorch、ONNX等常见格式的模型性能&#xff0c;无法运行rknn格式。考虑到YOLOv5模型转换为rknn会有一定的精度损失&#xff0c;但是需要具体数值才能进行评估&#xff0c;所以需要一个…

通过三角形相似原理实现单目测距

根据三角形相似原理计算相机焦距&#xff0c;公式为&#xff1a;F (P * D) / W 其中&#xff1a; F: 待求的相机的焦距 P: 图像中目标的宽度&#xff0c;单位像素 D: 真实目标与相机的距离&#xff0c;单位厘米 W: 真实目标的宽度&#xff0c;单位厘米 计算焦距前&#xff0c;…

太原理工大学Python数据分析原理与应用(课外考题:8~11章)

这部分大概只考10分&#xff0c;且大部分出在选择题&#xff0c;填空最多一两个 (仅供参考) 第十章 (理解概念为主&#xff0c;无需看推导过程) 第十一章