十大国外室内设计网站,教育网站制作设计,重庆百度开户,排行网页游戏个人主页~ 源码在Gitee仓库~ 上一篇贪吃蛇#xff08;上#xff09;~ 贪吃蛇 四、核心的实现游戏测试1、GameStart#xff08;1#xff09;控制台窗口大小和名字设置#xff08;2#xff09;光标隐藏#xff08;3#xff09;打印欢迎界面#xff08;4#xff09;创建… 个人主页~ 源码在Gitee仓库~ 上一篇贪吃蛇上~ 贪吃蛇 四、核心的实现游戏测试1、GameStart1控制台窗口大小和名字设置2光标隐藏3打印欢迎界面4创建地图5初始化蛇6创建第一个食物最终的GameStart 2、GameRun1定义一个宏来检测按键状态2PrintHelpInfo3SnakeMove4NextIsFood5EatFood6NoFood7KillBySelf8KillByWall最终的GameRun 3、GameEnd 五、源代码拷贝Snake.hSnake.cgame.h实际运行 四、核心的实现
游戏测试
#include locale.h
void test()
{int ch 0;srand((unsigned int)time(NULL));//时间戳用来实现随机数do{Snake snake { 0 };GameStart(snake);GameRun(snake);GameEnd(snake);SetPos(20, 15);printf(再来⼀局吗(Y/N):);ch getchar();getchar();} while (ch Y);SetPos(0, 27);}
int main()
{//修改当前地区为本地模式为了⽀持中⽂宽字符的打印setlocale(LC_ALL, );//测试逻辑test();return 0;
}1、GameStart
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打印欢迎界面
void WelcomeToGame()
{SetPos(40, 15);printf(欢迎来到贪吃蛇⼩游戏);SetPos(40, 25);// “按任意键继续”的出现的位置system(pause);//可以让页面暂停在这个位置直到用户按下一个键system(cls);//清除屏幕SetPos(25, 12);printf(⽤ ↑ . ↓ . ← . → 分别控制蛇的移动 F3为加速F4为减速\n);SetPos(25, 13);printf(加速将能得到更⾼的分数。\n);SetPos(40, 25);// “按任意键继续”的出现的位置这里可以让文字出现的位置看起来比较美观system(pause);system(cls);
}4创建地图
组成地图的小格子需要用宽字符打印 58行就打印29次
#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);}
}5初始化蛇
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(malloc fail);exit(1);}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-_food_weight 10;//每个食物的分数ps-_sleep_time 200;//每两次打印蛇身的间隔也就是蛇身的速度ps-_sorce 0;//初始总分数ps-_status OK;//蛇的初始状态
}6创建第一个食物
void CreateFood(pSnake ps)
{int x 0;int y 0;
again: do{x rand() % 53 2;//我们要的x坐标值介于2~54间任意数%53得到的值介于0~52加上2就在2~56范围y rand() % 25 1;//我们要的y坐标值介于1~25间任意数%25得到的值介于0~24加上1就在1~25范围//随机数时间戳根据时间计算的数据由于时间是不可修改切没有相同时候的所以它产生的数字被认为是随机数} while (x % 2 ! 0);pSnakeNode cur ps-_pSnake;//记录蛇头结点while (cur){if (x cur-x y cur-y){goto again;}cur cur-next;}//如果食物与蛇身上某一节点重合了则回到again处重新生成pSnakeNode pFood (pSnakeNode)malloc(sizeof(SnakeNode));//不重合就生成食物if (pFood NULL){perror(malloc fail);exit(2);}pFood-x x;pFood-y y;pFood-next NULL;SetPos(x, y);wprintf(L%lc, FOOD);//在对应位置处打印食物ps-_pFood pFood;
}最终的GameStart
void GameStart(pSnake ps)
{//设置控制台窗⼝的⼤⼩30⾏100列//mode 为DOS命令system(mode con cols100 lines30);//设置cmd窗⼝名称system(title 贪吃蛇); //获取标准输出的句柄(⽤来标识不同设备的数值)HANDLE hOutput GetStdHandle(STD_OUTPUT_HANDLE);//影藏光标操作CONSOLE_CURSOR_INFO CursorInfo;//获取控制台光标信息GetConsoleCursorInfo(hOutput, CursorInfo);//隐藏控制台光标CursorInfo.bVisible false; //设置控制台光标状态SetConsoleCursorInfo(hOutput, CursorInfo);//打印欢迎界⾯WelcomeToGame();//打印地图CreateMap();//初始化蛇InitSnake(ps);//创造第⼀个⻝物CreateFood(ps);
}2、GameRun
1定义一个宏来检测按键状态
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)0x1) ? 1 : 0)在上一篇博文中我们介绍了GetAsyncKeyState函数我们封装一个宏可以判断某个键是否被按下
2PrintHelpInfo
void PrintHelpInfo()
{SetPos(64, 14);wprintf(L%ls, L不能穿墙不能咬到自己);SetPos(64, 16);wprintf(L%ls, L用 ↑. ↓ . ← . → 来控制蛇的移动);SetPos(64, 18);wprintf(L%ls, L按F3加速F4减速);SetPos(64, 20);wprintf(L%ls, L按ESC退出游戏按空格暂停游戏);SetPos(64, 24);wprintf(L%ls, Ls_little_monster_倾情制作);
}3SnakeMove
void SnakeMove(pSnake ps)
{pSnakeNode pNextNode (pSnakeNode)malloc(sizeof(SnakeNode));//开辟预测的下一个节点if (pNextNode NULL){perror(malloc fail);exit(3);}switch (ps-_dir)//用switch语句判断此时蛇的走向{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;//注意左右移动是加减2pNextNode-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);}//判断下一个位置是否为食物是则进入EatFood不是则进入NoFoodKillBySelf(ps);KillByWall(ps);//检查是否撞到自己或墙而死亡
}4NextIsFood
int NextIsFood(pSnakeNode pn,pSnake ps)
{return (ps-_pFood-x pn-x ps-_pFood-y pn-y);//如果当前位置是食物则返回非0值不是返回0
}5EatFood
void EatFood(pSnakeNode pn, pSnake ps)
{ps-_pFood-next ps-_pSnake;ps-_pSnake ps-_pFood;//吃掉食物让食物成为蛇的头节点free(pn);//因为节点pn与节点_pFood是一个节点所以free掉其中的一个pn NULL;pSnakeNode cur ps-_pSnake;while (cur){SetPos(cur-x, cur-y);wprintf(L%lc, BODY);cur cur-next;}//打印蛇身ps-_sorce ps-_food_weight;//加分CreateFood(ps);//重新制造食物
}6NoFood
void NoFood(pSnakeNode pn, pSnake ps)
{pn-next ps-_pSnake;ps-_pSnake pn;//同EatFood将下一节点成为头结点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);//free掉尾节点cur-next NULL;
}7KillBySelf
void KillBySelf(pSnake ps)
{pSnakeNode pur ps-_pSnake-next;while (pur){//当此时的蛇头位置与蛇身某一节点重合时if (pur-x ps-_pSnake-x pur-y ps-_pSnake-y){ps-_status KILL_BY_SELF;//修改状态为 KILL_BY_SELFbreak;}pur pur-next;}
}8KillByWall
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;//修改状态为 KILL_BY_WALL}
}最终的GameRun
void GameRun(pSnake ps)
{PrintHelpInfo();//游戏玩法帮助打印do{SetPos(64, 10);printf(总分数%d\n, ps-_sorce);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))//F3为加速休眠时间变少也就是蛇的速度变快每个食物分数增加2{if(ps-_sleep_time 80)//速度不能太快{ps-_sleep_time - 30;ps-_food_weight 2;}}else if (KEY_PRESS(VK_F4))//F4为减速休眠时间变多也就是蛇的速度变慢每个食物分数减少2{if(ps-_food_weight 2)//食物分数要在2分以上{ps-_sleep_time 30;ps-_food_weight - 2;}}SnakeMove(ps);//蛇每走一步要进行的活动Sleep(ps-_sleep_time);//走一步休眠的时间也就是蛇的速度} while (ps-_status OK);//当游戏状态为OK时循环继续
}3、GameEnd
当游戏状态不为OK时告知游戏结束的原因并释放蛇身
void GameEnd(pSnake ps)
{SetPos(24, 12);switch (ps-_status){case END_NORMAL:wprintf(L主动结束游戏);break;case KILL_BY_SELF:wprintf(L撞到自己了,游戏结束);break;case KILL_BY_WALL:wprintf(L撞到墙了游戏结束);break;}pSnakeNode pur ps-_pSnake;while (pur){pSnakeNode del pur;pur pur-next;free(del);}
}五、源代码拷贝
Snake.h
#pragma once#include stdio.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★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 Snake
{pSnakeNode _pSnake;pSnakeNode _pFood;enum DRECTION _dir;enum GAME_STATUS _status;int _food_weight;int _sorce;int _sleep_time;
}Snake,*pSnake;void SetPos(short x, short y);void GameStart(pSnake ps);void CreateMap();void WelcomeToGame();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 KillByWall(pSnake ps);void KillBySelf(pSnake ps);void GameEnd(pSnake ps);Snake.c
#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(malloc fail);exit(1);}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-_food_weight 10;//每个食物的分数ps-_sleep_time 200;//每两次打印蛇身的间隔也就是蛇身的速度ps-_sorce 0;//初始总分数ps-_status OK;//蛇的初始状态
}void CreateFood(pSnake ps)
{int x 0;int y 0;
again: do{x rand() % 53 2;//我们要的x坐标值介于2~54间任意数%53得到的值介于0~52加上2就在2~56范围y rand() % 25 1;//我们要的y坐标值介于1~25间任意数%25得到的值介于0~24加上1就在1~25范围//随机数时间戳根据时间计算的数据由于时间是不可修改切没有相同时候的所以它产生的数字被认为是随机数} while (x % 2 ! 0);pSnakeNode cur ps-_pSnake;//记录蛇头结点while (cur){if (x cur-x y cur-y){goto again;}cur cur-next;}//如果食物与蛇身上某一节点重合了则回到again处重新生成pSnakeNode pFood (pSnakeNode)malloc(sizeof(SnakeNode));//不重合就生成食物if (pFood NULL){perror(malloc fail);exit(2);}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 cols100 lines30);system(title 贪吃蛇);HANDLE houtput GetStdHandle(STD_OUTPUT_HANDLE);//隐藏光标CONSOLE_CURSOR_INFO CursorInfo;GetConsoleCursorInfo(houtput, CursorInfo);CursorInfo.bVisible false;SetConsoleCursorInfo(houtput, CursorInfo);WelcomeToGame();CreateMap();InitSnake(ps);CreateFood(ps);}void PrintHelpInfo()
{SetPos(64, 14);wprintf(L%ls, L不能穿墙不能咬到自己);SetPos(64, 16);wprintf(L%ls, L用 ↑. ↓ . ← . → 来控制蛇的移动);SetPos(64, 18);wprintf(L%ls, L按F3加速F4减速);SetPos(64, 20);wprintf(L%ls, L按ESC退出游戏按空格暂停游戏);SetPos(64, 24);wprintf(L%ls, Ls_little_monster_倾情制作);
}#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);//如果当前位置是食物则返回非0值不是返回0
}void EatFood(pSnakeNode pn, pSnake ps)
{ps-_pFood-next ps-_pSnake;ps-_pSnake ps-_pFood;//吃掉食物让食物成为蛇的头节点free(pn);//因为节点pn与节点_pFood是一个节点所以free掉其中的一个pn NULL;pSnakeNode cur ps-_pSnake;while (cur){SetPos(cur-x, cur-y);wprintf(L%lc, BODY);cur cur-next;}//打印蛇身ps-_sorce ps-_food_weight;//加分CreateFood(ps);//重新制造食物
}void NoFood(pSnakeNode pn, pSnake ps)
{pn-next ps-_pSnake;ps-_pSnake pn;//同EatFood将下一节点成为头结点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);//free掉尾节点cur-next NULL;
}void KillBySelf(pSnake ps)
{pSnakeNode pur ps-_pSnake-next;while (pur){//当此时的蛇头位置与蛇身某一节点重合时if (pur-x ps-_pSnake-x pur-y ps-_pSnake-y){ps-_status KILL_BY_SELF;//修改状态为 KILL_BY_SELFbreak;}pur pur-next;}
}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;//修改状态为 KILL_BY_WALL}
}void SnakeMove(pSnake ps)
{pSnakeNode pNextNode (pSnakeNode)malloc(sizeof(SnakeNode));//开辟预测的下一个节点if (pNextNode NULL){perror(malloc fail);exit(3);}switch (ps-_dir)//用switch语句判断此时蛇的走向{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;//注意左右移动是加减2pNextNode-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);}//判断下一个位置是否为食物是则进入EatFood不是则进入NoFoodKillBySelf(ps);KillByWall(ps);//检查是否撞到自己或墙而死亡
}void GameRun(pSnake ps)
{PrintHelpInfo();//游戏玩法帮助打印do{SetPos(64, 10);printf(总分数%d\n, ps-_sorce);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))//F3为加速休眠时间变少也就是蛇的速度变快每个食物分数增加2{if(ps-_sleep_time 80)//速度不能太快{ps-_sleep_time - 30;ps-_food_weight 2;}}else if (KEY_PRESS(VK_F4))//F4为减速休眠时间变多也就是蛇的速度变慢每个食物分数减少2{if(ps-_food_weight 2)//食物分数要在2分以上{ps-_sleep_time 30;ps-_food_weight - 2;}}SnakeMove(ps);//蛇每走一步要进行的活动Sleep(ps-_sleep_time);//走一步休眠的时间也就是蛇的速度} while (ps-_status OK);//当游戏状态为OK时循环继续
}void GameEnd(pSnake ps)
{SetPos(24, 12);switch (ps-_status){case END_NORMAL:wprintf(L主动结束游戏);break;case KILL_BY_SELF:wprintf(L撞到自己了,游戏结束);break;case KILL_BY_WALL:wprintf(L撞到墙了游戏结束);break;}pSnakeNode pur ps-_pSnake;while (pur){pSnakeNode del pur;pur pur-next;free(del);}
}game.h
#include snake.h
#include locale.h
void test()
{int c 0;do{system(cls);Snake snake { 0 };GameStart(snake);GameRun(snake);GameEnd(snake);SetPos(20, 15);printf(再来一局吗?(Y/N):);c getchar();while (getchar() ! \n);} while (c Y);SetPos(0, 27);
}int main()
{setlocale(LC_ALL, );//本地化srand((unsigned int)time(NULL));//时间戳随机数test();return 0;
}实际运行 贪吃蛇的实际运行 今日分享就到这里了~