佛山市建设行政主管部门网站/百度快照和广告的区别
朋友们!好久不见。经过一段时间的沉淀,我这篇文章来和大家分享贪吃蛇大作战这个游戏是怎么实现的。
(一).贪吃蛇背景了解及效果展示
首先相信贪吃蛇游戏绝对称的上是我们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;
}
好了,兄弟们以上就是贪吃蛇项目的全部内容,通过去写这个项目我自己反正是感悟挺大的,因为从学习到自己实现这个过程虽然很艰苦,但当自己写出来能实现的时候觉得一切都值得,以后的编程之路一定会使我越来越牛掰,一起加油,兄弟们!
相关文章:

贪吃蛇大作战(C语言--实战项目)
朋友们!好久不见。经过一段时间的沉淀,我这篇文章来和大家分享贪吃蛇大作战这个游戏是怎么实现的。 (一).贪吃蛇背景了解及效果展示 首先相信贪吃蛇游戏绝对称的上是我们00后的童年,不仅是贪吃蛇还有俄罗斯⽅块&…...

谷歌确认:链接并不那么重要
谷歌的 Gary Illyes 在最近的一次搜索营销会议上证实,谷歌只需要很少的链接,这为出版商需要关注其他因素提供了越来越多的证据。Gary 在推特上证实了他确实说过这些话。 排名链接的背景 20 世纪 90 年代末,搜索引擎发现链接是验证网站权威性…...

python基础--修饰器
修饰器(语法糖) 在python中函数实际上就是一个对象 def outer(x):def inner(y):return x yreturn innerprint(outer(6)(5))def double(x):return x * 2 def triple(x):return x * 3def calc_number(func, x):print(func(x))calc_number(double, 3) calc_number(triple, 3)函…...

6. Z 字形变换
题目描述 给你一个字符串s和行数numRows,把s字符串按照z字形重新排列。 再从左往右进行读取,返回读取之后的字符串。 本题是找规律,但是没有找出来 解题思路 要想解出来该题,在进行z字变换的时候,我们把字符串的下…...

shell常用文件处理命令
1. 解压 1.1 tar 和 gz 文件 如果你有一个 .tar 文件,你可以使用以下命令来解压: tar -xvf your_file.tar在这个命令中,-x 表示解压缩,-v 表示详细输出(可选),-f 后面跟着要解压的文件名。 如果你的 .tar 文件同时被 gzip 压缩了(即 .tar.gz 文件),你可以使用以下…...

从Paint 3D入门glTF
Paint 3D Microsoft Paint 3D是微软的一款图像编辑软件,它是传统的Microsoft Paint程序的升级版。 这个新版本的Paint专注于三维设计和创作,使用户可以使用简单的工具创建和编辑三维模型。 Microsoft Paint 3D具有直观的界面和易于使用的工具࿰…...

数据库(MySQL)—— DQL语句(基本查询和条件查询)
数据库(MySQL)—— DQL语句(基本查询和条件查询) 什么是DQL语句基本查询查询多个字段字段设置别名去除重复记录 条件查询语法条件 我们今天进入MySQL的DQL语句的学习: 什么是DQL语句 MySQL中的DQL(Data Q…...

如何根据索引删除数组中的元素,并保证删除的正确性
使用 splice() 方法来删除这些索引处的数据 var array [1, 2, 3, 4, 5]; var indexesToDelete [1, 3]; // 需要删除的索引// 将需要删除的索引按照从大到小的顺序排序,以避免删除元素后索引发生变化 indexesToDelete.sort((a, b) > b - a);// 遍历需要删除的索…...

Shell编程规范与变量
目录 一、shell脚本概述 Shell脚本的概念 Shel脚本应用场景 1、shell的作用 2、shell编程规范 Shell脚本的编写 Shell脚本的运行 3、重定向与管道 交互式硬件设备 重定向操作 管道操作符号"|" 二、shell脚本变量 变量的作用 变量的类型 1、自定义变量…...

武汉星起航:策略升级,亚马逊平台销售额持续增长显实力
武汉星起航电子商务有限公司,一家致力于跨境电商领域的企业,于2023年10月30日在上海股权托管交易中心成功挂牌展示,这一里程碑事件标志着公司正式踏入资本市场,开启了新的发展篇章。公司董事长张振邦在接受【第一财经】采访时表示…...

循环链表 -- c语言实现
#pragma once // 带头双向循环链表增删查改实现 #include<stdlib.h> #include<stdio.h> #include<assert.h>typedef int LTDataType;typedef struct ListNode {LTDataType data;struct ListNode* next;struct ListNode* prev; }ListNode;//双链表申请一个新节…...

如何使git提交的时候忽略一些特殊文件?
认识.gitignore文件 在生成远程仓库的时候我们会看到这样一个选项: 这个.gitignore文件有啥用呢? .gotignore文件是Git版本控制系统中的一个特殊文件。用来指定哪些文件或者目录不被Git追踪或者提交到版本库中。也就意味着,如果我们有一些文…...

如何保证Redis双写一致性?
目录 数据不一致问题 数据库和缓存不一致解决方案 1. 先更新缓存,再更新数据 该方案数据不一致的原因 2. 先更新数据库,再更新缓存 3. 先删除缓存,再更新数据库 延时双删 4. 先更新数据库,再删除缓存 该方案数据不一致的…...

HarmonyOS实战开发-如何实现查询当前城市实时天气功能
先来看一下效果 本项目界面搭建基于ArkUI中TS扩展的声明式开发范式, 数据接口是和风(天气预报), 使用ArkUI自带的网络请求调用接口。 我想要实现的一个功能是,查询当前城市的实时天气, 目前已实现的功能…...

(三)JSP教程——JSP动作标签
JSP动作标签 用户可以使用JSP动作标签向当前输出流输出数据,进行页面定向,也可以通过动作标签使用、修改和创建对象。 <jsp:include>标签 <jsp:include>标签将同一个Web应用中静态或动态资源包含到当前页面中。资源可以是HTML、JSP页面和文…...

centos7安装真的Redmine-5.1.2+ruby-3.0.0
下载redmine-5.1.2.tar.gz,上传到/usr/local/目录下 cd /usr/local/ tar -zxf redmine-5.1.2.tar.gz cd redmine-5.1.2 cp config/database.yml.example config/database.yml 配置数据连接 #编辑配置文件 vi config/database.yml #修改后的内容如下 product…...

方法的重写
方法的重写 概念:子类继承父类之后,就拥有了符合权限的父类的属性和方法,但是当父类的方法不符合子类的要求的时候,子类也可以重新的书写自己想要的方法。所以,方法的重写,即子类继承父类的方法后…...

Terraform局部值
Terraform输入变量用于从外部传递值到Terraform模块内部进行使用,如果把Terraform代码看作是一个函数的话,Terraform输入变量就是函数的输入参数。 Terraform局部值则用于在Terraform模块内部定义反复使用的常量值或表达式,如果把Terraform代…...

vue+element-ui实现横向长箭头,横向线上下可自定义文字(使用after伪元素实现箭头)
项目场景: 需要实现一个长箭头,横向线上下可自定义文字 代码描述 <div><span class"data-model">{{ //上方文字}}</span><el-divider class"q"> </el-divider>//分隔线<span class"data-mod…...

性能监控之prometheus+grafana搭建
前言 Prometheus和Grafana是两个流行的开源工具,用于监控和可视化系统和应用程序的性能指标。它们通常一起使用,提供了强大的监控和数据可视化功能。 Prometheus Prometheus是一种开源的系统监控和警报工具包。它最初由SoundCloud开发,并于…...

25-ESP32-S3 内置的真随机数发生器(RNG)
ESP32-S3 内置的真随机数发生器(RNG)😎 引言 📚 在许多应用中,随机数发生器(RNG)是必不可少的。无论是在密码学🔒、游戏🎮、模拟🧪或其他领域,随…...

万兆以太网MAC设计(12)万兆UDP协议栈上板与主机网卡通信
文章目录 一、设置IP以及MAC二、上板效果2.1、板卡与主机数据回环测试2.2、板卡满带宽发送数据 一、设置IP以及MAC 顶层模块设置源MAC地址 module XC7Z100_Top#(parameter P_SRC_MAC 48h01_02_03_04_05_06,parameter P_DST_MAC 48hff_ff_ff_ff_ff_ff )(input …...

2024年4月17日华为春招实习试题【三题】-题目+题解+在线评测,2024.4.17,华为机试
2024年4月17日华为春招实习试题【三题】-题目题解在线评测 🔮题目一描述:扑克牌消消乐输入描述输出描述样例一样例二Limitation解题思路一:模拟,遇到连续3张相同牌号的卡牌,直接删除解题思路二:栈解题思路三…...

展开说说:Android线程池解析
何谓线程池?本人理解是存放和管理线程的一个容器。 线程池存在的意义是什么? 第一:前面博客提到过创建和销毁线程的操作本身是有性能开销的,如果把使用的线程对象存起来下次用的时候直接取出来用就省去了一次创建和销毁的成本&a…...

Selenium自动化测试面试题全家桶
🔥 交流讨论:欢迎加入我们一起学习! 🔥 资源分享:耗时200小时精选的「软件测试」资料包 🔥 教程推荐:火遍全网的《软件测试》教程 📢欢迎点赞 👍 收藏 ⭐留言 …...

Docker 容器日志占用空间过大解决办法
1、vi /etc/docker/daemon.json {"log-driver":"json-file","log-opts": {"max-size":"200m", "max-file":"1"} } 2、重新加载守护进程配置文件 systemctl daemon-reload 3、重启docker systemctl…...

update_min_vruntime()流程图
linux kernel scheduler cfs的update_min_vruntime() 看起来还挺绕的。含义其实也简单,总一句话,将 cfs_rq->min_vruntime 设置为: max( cfs_rq->vruntime, min(leftmost_se->vruntime, cfs_rq->curr->vruntime) )。 画个流…...

十进制转任意进制(以及任意进制来回转换<了解>)
十进制转任意进制: #include <iostream> #include <vector> #include <string> using namespace std; // 将十进制数转换为P进制形式的字符串 string toBase(int num, int base) {string result ""; // 初始化结果字符串为空wh…...

postcss-px-to-viewport 从入坑到放弃 (nuxt3搭建响应式官网解决方案 )
前沿 什么是 postcss-px-to-viewport 将px单位转换为视口单位的 (vw, vh, vmin, vmax) 的 PostCSS 插件。 为什么使用 postcss-px-to-viewport 在pc端盛行的时代 ,如果你不想去适配更多的pc端代码,可以采用它。 由于nuxt3本身已带postcss,所…...

C语言从入门到入门
一、引言 C语言是一种通用的、过程式的计算机编程语言,支持结构化编程、词汇变量作用域和递归等功能,其设计提供了低级别的存取权限,并且要求程序员管理所有的内存细节。C语言具有高效、灵活和可移植性等特点,因此被广泛应用于系统编程、嵌入式系统开发、游戏开发等领域。 …...