免费获客软件,汕头seo外包,网页制作工具哪个好用,电子菜单小程序怎么做本篇博客梳理贪吃蛇小游戏具体的代码实现 游戏代码文件分布逻辑如下#xff1a;
Snake.h#xff1a;标准库头文件的包含#xff0c;类型的声明#xff0c;函数的声明Snake.c#xff1a;游戏功能的具体实现test.c#xff1a;游戏的测试逻辑 下面来进行贪吃蛇的具体代码实…本篇博客梳理贪吃蛇小游戏具体的代码实现 游戏代码文件分布逻辑如下
Snake.h标准库头文件的包含类型的声明函数的声明Snake.c游戏功能的具体实现test.c游戏的测试逻辑 下面来进行贪吃蛇的具体代码实现
一、游戏开始运行前准备-GameStart
函数原型如下
void GameStart(pSnake ps); void GameStart(pSnake ps)
{ //1.设置游戏窗口的大小与窗口名字system(mode con cols100 lines30); system(title 贪吃蛇); //2.隐藏屏幕光标HANDLE hOutput GetStdHandle(STD_OUTPUT_HANDLE); //获取标准输出的句柄CONSOLE_CURSOR_INFO CursorInfo; //创建光标信息结构体GetConsoleCursorInfo(hOutput, CursorInfo);//获取控制台光标信息 CursorInfo.bVisible false; //隐藏控制台光标 SetConsoleCursorInfo(hOutput, CursorInfo);//设置控制台光标状态 //3.打印欢迎界⾯ WelcomeToGame(); //4.创建地图 CreateMap(); //5.初始化蛇身 InitSnake(ps); //6.创造⻝物 CreateFood(ps);
} 1打印欢迎界面WelcomeToGame函数
逻辑定位光标-打印-清屏
void WelcomeToGame()
{SetPos(40, 14);wprintf(L欢迎来到贪吃蛇小游戏\n);SetPos(42, 20);system(pause);system(cls);SetPos(25, 14);wprintf(L用 ↑. ↓ . ← . → 来控制蛇的移动按F3加速F4减速\n);SetPos(25, 15);wprintf(L加速能够得到更高的分数\n);SetPos(42, 20);system(pause);system(cls);
}注SetPos定位的坐标可以自行调整目的是为了打印的布局好看一些
2创建地图CreateMap函数
逻辑实现27行58列包括墙体在内的地图在头文件中定义一下墙的符号会更为方便 在头文件中定义
#define WALL L□void CreateMap()
{//上边界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 25; i){SetPos(0, i);wprintf(L%lc, WALL);}//右边界for (i 1; i 25; i){SetPos(56, i);wprintf(L%lc, WALL);}
}3初始化蛇身InitSnake函数
逻辑将指向蛇的指针传给这个函数并作如下操作
1创建一条五个节点的蛇
2打印蛇
3设置蛇的属性
void InitSnake(pSnake ps)
{int i 0;pSnakeNode cur NULL;//创建蛇身的五个节点for (i 0; i 5; i){cur (pSnakeNode)malloc(sizeof(SnakeNode));if (cur NULL){perror(InitSnake()::malloc());return;}cur-next NULL;cur-x POS_X 2 * i;//POS_X在头文件中声明的初始x坐标cur-y POS_Y;//POS_Y在头文件中声明的初始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;//当前分数是0分ps-_food_weight 10;//每个食物10分ps-_sleep_time 200;//单位是毫秒ps-_status OK;}4创建食物CreateFood函数
逻辑如下
(1)随机生成x和yx的范围是[2,54]y的范围是[1,25]
(2)确定x和y不能和蛇身坐标冲突
(3)创建食物的节点
食物节点直接利用蛇的节点即可只不过要把其中的next指针置为NULL这样子就不用创建新的类型了
void CreateFood(pSnake ps)
{int x 0;int y 0;//特别注意生成的x是2的倍数//x2~54//y: 1~25
again:do{x rand() % 53 2;y rand() % 25 1;} while (x % 2 ! 0);//遍历蛇看坐标有无冲突pSnakeNode cur ps-_pSnake;while (cur){if (x cur-x y cur-y){goto again;//如果生成的x,y和蛇身坐标冲突就回去继续生成新的x,y}cur cur-next;}//创建食物的节点pSnakeNode pFood (pSnakeNode)malloc(sizeof(SnakeNode));if (pFood NULL){perror(CreateFood()::malloc());return;}pFood-x x;pFood-y y;pFood-next NULL;SetPos(x, y);//定位食物的位置wprintf(L%lc, FOOD);ps-_pFood pFood;//把食物放到贪吃蛇结构中去维护
}二、游戏运行-GameRun
函数原型如下
void GameRun(pSnake ps);虚拟键值注明如下 • 上VK_UP • 下VK_DOWN • 左VK_LEFT • 右VK_RIGHT • 空格VK_SPACE • ESCVK_ESCAPE • F3VK_F3 • F4VK_F4
void GameRun(pSnake ps)
{PrintHelpInfo();//打印帮助信息do{//打印总分数和食物的分值SetPos(64, 10);printf(总分数:%d\n, ps-_score);SetPos(64, 11);printf(当前食物的分数:%2d\n, ps-_food_weight);
//获取按键情况KEY_PRESSif (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);
}1打印说明信息PrintHelpInfo函数
void PrintHelpInfo()
{SetPos(64, 14);wprintf(L%ls, L不能穿墙不能咬到自己);SetPos(64, 15);wprintf(L%ls, L用 ↑. ↓ . ← . → 来控制蛇的移动);SetPos(64, 16);wprintf(L%ls, L按F3加速F4减速);SetPos(64, 17);wprintf(L%ls, L按ESC退出游戏按空格暂停游戏);
}2根据按键情况移动蛇SnakeMove函数
void SnakeMove(pSnake ps)
{//创建一个结点表示蛇即将到的下一个节点pSnakeNode pNextNode (pSnakeNode)malloc(sizeof(SnakeNode));if (pNextNode NULL){perror(SnakeMove()::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);}//检测蛇是否撞墙KilledByWall(ps);//检测蛇是否撞到自己KilledBySelf(ps);
}1判断下一个节点是不是食物NextIsFood函数
是就返回1不是就返回0
int NextIsFood(pSnakeNode pn, pSnake ps)
{return (ps-_pFood-x pn-x ps-_pFood-y pn-y);
}①是吃掉食物并作相应处理——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;//重新创建新的食物CreateFood(ps);
}②不是尾巴删除一节——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);//把倒数第二个节点的地址置为NULLcur-next NULL;
}2判断是否撞墙KilledByWall
void KilledByWall(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判断是否撞上自己KilledBySelf
void KilledBySelf(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;break;}cur cur-next;}
}4暂停Pause函数
void Pause()
{while (1){Sleep(200);if (KEY_PRESS(VK_SPACE)){break;}}
}三、游戏结束善后工作-GameEnd函数
void GameEnd(pSnake ps)
{SetPos(24, 12);switch (ps-_status){case END_NORMAL:wprintf(L您主动结束游戏\n);break;case KILL_BY_WALL:wprintf(L您撞到墙上游戏结束\n);break;case KILL_BY_SELF:wprintf(L您撞到了自己游戏结束\n);break;}//释放蛇身的链表pSnakeNode cur ps-_pSnake;while (cur){pSnakeNode del cur;cur cur-next;free(del);}
}四、完整代码展示
Snake.h
#pragma once#include windows.h
#include stdbool.h
#include stdlib.h
#include stdio.h
#include time.h#define POS_X 24
#define POS_Y 5#define WALL L□
#define BODY L●
#define FOOD L★//类型的声明//蛇的方向
enum DIRECTION
{UP 1,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 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();//创建地图
void CreateMap();//初始化蛇身
void InitSnake(pSnake ps);//创建食物
void CreateFood(pSnake ps);//游戏运行的逻辑
void GameRun(pSnake ps);//蛇的移动-走一步
void SnakeMove(pSnake ps);//判断下一个坐标是否是食物
int NextIsFood(pSnakeNode pn, pSnake ps);//下一个位置是食物就吃掉食物
void EatFood(pSnakeNode pn, pSnake ps);//下一个位置不是食物
void NoFood(pSnakeNode pn, pSnake ps);//检测蛇是否撞墙
void KilledByWall(pSnake ps);//检测蛇是否撞到自己
void KilledBySelf(pSnake ps);//游戏善后的工作
void GameEnd(pSnake ps);Snake.c
#define _CRT_SECURE_NO_WARNINGS 1#include snake.hvoid SetPos(short x, short y)
{//获得标准输出设备的句柄HANDLE houtput NULL;houtput GetStdHandle(STD_OUTPUT_HANDLE);//定位光标的位置COORD pos { x, y };SetConsoleCursorPosition(houtput, pos);
}void WelcomeToGame()
{SetPos(40, 14);wprintf(L欢迎来到贪吃蛇小游戏\n);SetPos(42, 20);system(pause);system(cls);SetPos(25, 14);wprintf(L用 ↑. ↓ . ← . → 来控制蛇的移动按F3加速F4减速\n);SetPos(25, 15);wprintf(L加速能够得到更高的分数\n);SetPos(42, 20);system(pause);system(cls);
}void CreateMap()
{//上边界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 25; i){SetPos(0, i);wprintf(L%lc, WALL);}//右边界for (i 1; i 25; i){SetPos(56, i);wprintf(L%lc, WALL);}
}void InitSnake(pSnake ps)
{int i 0;pSnakeNode cur NULL;for (i 0; i 5; i){cur (pSnakeNode)malloc(sizeof(SnakeNode));if (cur NULL){perror(InitSnake()::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 CreateFood(pSnake ps)
{int x 0;int y 0;//生成x是2的倍数//x2~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(CreateFood()::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)
{//0. 先设置窗口的大小再光标隐藏system(mode con cols100 lines30);system(title 贪吃蛇);HANDLE houtput GetStdHandle(STD_OUTPUT_HANDLE);//影藏光标操作CONSOLE_CURSOR_INFO CursorInfo;GetConsoleCursorInfo(houtput, CursorInfo);//获取控制台光标信息CursorInfo.bVisible false; //隐藏控制台光标SetConsoleCursorInfo(houtput, CursorInfo);//设置控制台光标状态//1. 打印环境界面和功能介绍WelcomeToGame();//2. 绘制地图CreateMap();//3. 创建蛇InitSnake(ps);//4. 创建食物CreateFood(ps);
}void PrintHelpInfo()
{SetPos(64, 14);wprintf(L%ls, L不能穿墙不能咬到自己);SetPos(64, 15);wprintf(L%ls, L用 ↑. ↓ . ← . → 来控制蛇的移动);SetPos(64, 16);wprintf(L%ls, L按F3加速F4减速);SetPos(64, 17);wprintf(L%ls, L按ESC退出游戏按空格暂停游戏);}#define KEY_PRESS(vk) ((GetAsyncKeyState(vk)1)?1:0)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;//重新创建食物CreateFood(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);//把倒数第二个节点的地址置为NULLcur-next NULL;
}void KilledByWall(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 KilledBySelf(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;break;}cur cur-next;}
}void SnakeMove(pSnake ps)
{//创建一个结点表示蛇即将到的下一个节点pSnakeNode pNextNode (pSnakeNode)malloc(sizeof(SnakeNode));if (pNextNode NULL){perror(SnakeMove()::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);}//检测蛇是否撞墙KilledByWall(ps);//检测蛇是否撞到自己KilledBySelf(ps);
}void GameRun(pSnake ps)
{//打印帮助信息PrintHelpInfo();do{//打印总分数和食物的分值SetPos(64, 10);printf(总分数:%d\n, ps-_score);SetPos(64, 11);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)
{SetPos(24, 12);switch (ps-_status){case END_NORMAL:wprintf(L您主动结束游戏\n);break;case KILL_BY_WALL:wprintf(L您撞到墙上游戏结束\n);break;case KILL_BY_SELF:wprintf(L您撞到了自己游戏结束\n);break;}//释放蛇身的链表pSnakeNode cur ps-_pSnake;while (cur){pSnakeNode del cur;cur cur-next;free(del);}
}test.c
#define _CRT_SECURE_NO_WARNINGS 1#include locale.h
#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();while (getchar() ! \n);} while (ch Y || ch y);SetPos(0, 27);
}int main()
{//设置适配本地环境setlocale(LC_ALL, );srand((unsigned int)time(NULL));test();return 0;
}