邯郸市城乡住房建设局网站,教育行业网站建设审批,网站建设夬金手指排名壹陆,山西网站搜索排名优化公司❀创作不易#xff0c;关注作者不迷路❀#x1f600;#x1f600; 目录
#x1f600;贪吃蛇简介
#x1f603;贪吃蛇的实现
#x1f40d;生成地图
#x1f40d;生成蛇模块 ❀定义蛇的结构体
❀初始化蛇的相关信息
❀初始化食物的相关信息
#x1f40d;光标定位和… ❀创作不易关注作者不迷路❀ 目录
贪吃蛇简介
贪吃蛇的实现
生成地图
生成蛇模块 ❀定义蛇的结构体
❀初始化蛇的相关信息
❀初始化食物的相关信息
光标定位和隐藏
❀如何隐藏光标
❀如何定位光标
蛇的移动
❀Sleep()函数一个让程序暂停的函数
❀删除轨迹
控制键盘改变蛇的移动方向
吃食物获得积分
蛇死亡结束游戏
完整代码
拓展 贪吃蛇简介 贪吃蛇是一款经典的小游戏通过控制蛇头方向吃食物使得蛇变长从而获得积分通过上下左右键控制蛇的移动方向寻找吃的东西每吃到一口就能得到一定的积分而且蛇的身子会越来越长难度也越来越大不能碰墙也不能咬到自己的身体和尾巴否则游戏结束总体来说这款小游戏既简单又耐玩至今仍是一款风靡全球的小游戏也是编程入门学习的热门小游戏今天将带领大家实现这款小游戏 贪吃蛇的实现
生成地图
首先生成一张长为60宽为20的地图 生成地图只有在边界时输出“”其余地方输出“ ”
#define H 20
#define W 60void show_wall()
{for (int i 0; i H; i){for (int j 0; j W; j){if (i H || j W)printf();else printf( );}puts();}}
生成蛇模块 ❀定义蛇的结构体
typedef struct body {//记录坐标int x; int y;
}BODY;typedef struct snake {int size;//蛇的长度BODY list[W * H];//蛇的位置,最大可以铺满整个墙 BODY food;//定义BODY类型的food变量存储食物的位置信息COORD coord;//光标相关的结构体不需要自己写是本来就有的int dx;//x轴移动方向 int dy;//y轴移动方向 int score;//分数 BODY tail;//记录尾巴的位置后续蛇的移动需要
}SNAKE;
❀初始化蛇的相关信息
初始化时蛇的长度为2并且在地图最中间生成蛇
void init_snake(SNAKE* snake)
{snake-size 2;//初始化蛇的长度为2头和尾长度为2//使蛇在地图最中间生成snake-list[0].x W / 2; snake-list[1].x W / 2 - 1;snake-list[0].y H / 2; snake-list[1].y H / 2;snake-score 0;//初始分数为0snake-dx 1; snake-dy 0;//初始移动方向:向右移动init_food((snake-food));//初始化食物
}
❀初始化食物的相关信息
食物在地图内随机生成因此要调用随机数生成函数rand() 所需头文件
#includetime.h
void init_food(BODY* food)
{srand(time(NULL));food-x rand() % W;food-y rand() % H;while (food-x 0 || food-y 0){food-x rand() % W;food-y rand() % H;}
}
生成的食物x轴范围在(0,W-1]y轴范围在(0,H-1],但仅仅对W和H取模是有可能生成在边界(x0或y0)的食物因此生成时不满足条件则再次生成食物
光标定位和隐藏 打印地图之后我们看到光标在“按任意键关闭此窗口...”这里而且在不停的闪烁光标在地图中闪烁并不美观所以我们需要加以隐藏光标同时还需要定位光标到指定位置比如后续的蛇的移动食物的生成都需要光标定位
❀如何隐藏光标
头文件
#includeWindows.h
void hide_cur()//隐藏光标里面是windows自带的函数,不需要刻意去记也不需要自己去写拿来就用
{CONSOLE_CURSOR_INFO cci;cci.dwSize sizeof(cci);cci.bVisible FALSE;SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), cci);
}
上面的函数是Windows自带的函数用于隐藏光标在不同的系统中不一样Linux系统就跟这个不一样因此要用的时候查找资料就好
❀如何定位光标
地图已经有了我们发现打印完地图后光标位置在地图之外而蛇的初始位置是在地图中间那么怎么办使用光标定位让光标定位到地图中间然后打印蛇
void show_ui(SNAKE* snake)
{for (int i 0; i snake-size; i){ //定位光标位置,windows自带函数snake-coord.X snake-list[i].x; snake-coord.Y snake-list[i].y;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake-coord);if (i 0)printf();//蛇头用表示else printf(*);//蛇的身体用*表示}snake-coord.X snake-food.x; snake-coord.Y snake-food.y;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake-coord);printf(#);//食物用#表示snake-coord.X snake-tail.x; snake-coord.Y snake-tail.y;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake-coord);printf( );
}
每一次定位光标都需要写一遍这份代码 snake-coord.X snake-list[i].x; snake-coord.Y snake-list[i].y;//光标x,y轴分别指向的位置
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake-coord);
定位光标还需要用到系统自带结构体 COORD coord; OK,我们运行调试一下: 成功的使光标定位在想要的位置显示出食物和蛇但目前蛇并不能移动我们需要让蛇动起来因此我们下一步的目标便是让蛇移动
蛇的移动
对于蛇的移动我们可以将蛇头往前移一个单位然后蛇的身体跟着蛇头向前移动
void move_snake(SNAKE* snake)
{snake-tail.x snake-list[snake-size - 1].x;snake-tail.y snake-list[snake-size - 1].y;//蛇的移动倒过来赋值for (int i snake-size - 1; i 0; i--){snake-list[i].x snake-list[i - 1].x;snake-list[i].y snake-list[i - 1].y;}//蛇头的移动单独赋值snake-list[0].x snake-dx;snake-list[0].y snake-dy;
}
这里有一个小技巧就是赋值的时候要倒着赋值否则如果正向赋值即蛇头的位置前进1个单位这时候在赋值给蛇身体时只能赋值到蛇头的位置而其他的位置则被覆盖了倒过来赋值则不会出现这样的问题 此时运行结果如图这里有两个问题打开控制台就是一连串的*和因为程序的运行是很快的在几毫秒就执行完成了我们观察不了它如何移动的其次一连串的*是由于前面打印出来的*没有删除而残留下来的。接下来我们来解决这两个问题
❀Sleep()函数一个让程序暂停的函数
程序执行打印*号是很快的我们不想让它那么快打印我们应该让它打印一次就停顿一小会然后继续打印
void start_game(SNAKE* snake)
{while (snake-list[0].x 0 snake-list[0].x W - 1 snake-list[0].y 0 snake-list[0].y H - 1){show_ui(snake);control_move(snake);eat_food(snake);move_snake(snake);eat_snake_body(snake);Sleep(200);//让程序暂停200ms后再运行}game_over(snake);
}
运行结果 这一阶段实现了打印的停顿有了一个动态的效果但是每次移动前面的*号没有删除因此要删除轨迹
❀删除轨迹
如何删除轨迹可以每一次打印*号后光标移动到蛇尾在蛇尾的位置打印空格用于清除轨迹
snake-coord.X snake-tail.x; snake-coord.Y snake-tail.y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake-coord);
printf( );
运行结果 清除了轨迹之后蛇的移动效果便制作出来了目前移动的方向是向右实际还可以向上下左右移动下一步我们就开始实现用键盘控制改变移动方向
控制键盘改变蛇的移动方向
通过键盘按键改变移动方向我们设定w向上s(向下a向左)d向右
void control_move(SNAKE* snake)
{char key 0;while (_kbhit()){key _getch();}switch (key){case d:snake-dx 1;snake-dy 0;break;case s:snake-dx 0;snake-dy 1;break;case a:snake-dx -1;snake-dy 0;break;case w:snake-dx 0;snake-dy -1;break;}
}
获取键盘按键有专门的函数_kbhit()和_getch()当_kbhit()感应到键盘按下时通过_getch将获取键盘按下的值传给key
所需头文件
#includeconio.h 然后根据方向通过switchcase语句修改dx,dy从而做到改变方向
吃食物获得积分
吃到食物加10分身体长度加1然后通过随机函数再次生成新的食物
void eat_food(SNAKE* snake)
{if (snake-food.x snake-list[0].x snake-food.y snake-list[0].y){snake-size;snake-score 10;init_food(snake-food);}
}
蛇死亡结束游戏
在本游戏案例中结束游戏有两种情况:①蛇撞到墙②蛇吃到身体
蛇没撞到墙时一直在while()循环内撞到墙则退出循环游戏结束
void start_game(SNAKE* snake)
{while (snake-list[0].x 0 snake-list[0].x W - 1 snake-list[0].y 0 snake-list[0].y H - 1){show_ui(snake);control_move(snake);eat_food(snake);move_snake(snake);eat_snake_body(snake);Sleep(1000);}game_over(snake);//蛇撞到墙游戏结束
}
蛇吃到自己身体游戏结束
蛇头的坐标和身体部位的坐标相同时代表吃到身体通过for循环遍历寻找是否符合吃到身体这一情况
void eat_snake_body(SNAKE* snake)
{for (int i 1; i snake-size; i){if (snake-list[0].x snake-list[i].x snake-list[0].y snake-list[i].y){game_over(snake);exit(0);}}
}
完整代码
#includestdio.h
#includeWindows.h
#includestring.h
#includestdlib.h
#includeconio.h
#includetime.h
#define H 20
#define W 60
typedef struct body {int x; int y;
}BODY;typedef struct snake {int size;//蛇的长度BODY list[W * H];//蛇的位置,最大可以铺满整个墙 BODY food;COORD coord;int dx;//x轴移动方向 int dy;//y轴移动方向 int score;//分数 BODY tail;
}SNAKE;
//展示界面
void show_wall()
{for (int i 0; i H; i){for (int j 0; j W; j){if (i H || j W)printf();else printf( );}puts();}}
void init_food(BODY* food)
{srand(time(NULL));food-x rand() % W;food-y rand() % H;while (food-x 0 || food-y 0){food-x rand() % W;food-y rand() % H;}
}
void init_snake(SNAKE* snake)
{snake-size 2;snake-list[0].x W / 2; snake-list[1].x W / 2 - 1;snake-list[0].y H / 2; snake-list[1].y H / 2;snake-score 0;snake-dx 1; snake-dy 0;init_food((snake-food));
}
void hide_cur()//隐藏光标里面是windows特有的函数,不需要刻意去记拿来就用
{CONSOLE_CURSOR_INFO cci;cci.dwSize sizeof(cci);cci.bVisible FALSE;SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), cci);
}
void show_ui(SNAKE* snake)
{for (int i 0; i snake-size; i){ //定位光标位置,windows自带函数snake-coord.X snake-list[i].x; snake-coord.Y snake-list[i].y;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake-coord);if (i 0)printf();else printf(*);}snake-coord.X snake-food.x; snake-coord.Y snake-food.y;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake-coord);printf(#);snake-coord.X snake-tail.x; snake-coord.Y snake-tail.y;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake-coord);printf( );
}
void move_snake(SNAKE* snake)
{snake-tail.x snake-list[snake-size - 1].x;snake-tail.y snake-list[snake-size - 1].y;for (int i snake-size - 1; i 0; i--){snake-list[i].x snake-list[i - 1].x;snake-list[i].y snake-list[i - 1].y;}snake-list[0].x snake-dx;snake-list[0].y snake-dy;
}
void control_move(SNAKE* snake)
{char key 0;while (_kbhit()){key _getch();}switch (key){case d:snake-dx 1;snake-dy 0;break;case s:snake-dx 0;snake-dy 1;break;case a:snake-dx -1;snake-dy 0;break;case w:snake-dx 0;snake-dy -1;break;}
}
void eat_food(SNAKE* snake)
{if (snake-food.x snake-list[0].x snake-food.y snake-list[0].y){snake-size;snake-score 10;init_food(snake-food);}
}
void game_over(SNAKE* snake)
{snake-coord.X 30; snake-coord.Y 25;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake-coord);printf(游戏结束!!! 得分:%d, snake-score);system(pause);
}
void eat_snake_body(SNAKE* snake)
{for (int i 1; i snake-size; i){if (snake-list[0].x snake-list[i].x snake-list[0].y snake-list[i].y){game_over(snake);exit(0);}}
}
void start_game(SNAKE* snake)
{while (snake-list[0].x 0 snake-list[0].x W - 1 snake-list[0].y 0 snake-list[0].y H - 1){show_ui(snake);control_move(snake);eat_food(snake);move_snake(snake);eat_snake_body(snake);Sleep(300);}game_over(snake);
}int main()
{hide_cur();SNAKE* snake (SNAKE*)malloc(sizeof(SNAKE));init_snake(snake);show_wall();show_ui(snake);start_game(snake);free(snake);system(pause);return 0;
}
拓展
可以实现障碍物新增关卡等等以及图形化界面提升难度下列代码展示了增加障碍物 #includestdio.h
#includestring.h
#includestdlib.h
#includetime.h
#includeWindows.h
#includeconio.h/*
注意:
w:上移
s:下移
a:左移
d:右移
不可以用getchar()来进行控制,getchar()之后要有回车才行,而我们只希望通过wsad这些按键进行操作
但有专门的按键操作函数
#includeconio.h
char key;//key要给初始值,否则key没值,进不了循环,但初始值也不能是wasd等
while(_kbhit())//判断是否按下按键,按下不等于0
{key_getch();
}
*/char temp;
//1.设置地图边界
#define W 60
#define H 20//2.设置贪吃蛇结构体
typedef struct _body {int x; //坐标xint y; //坐标y
}BODY;typedef struct snake{BODY list[W * H];//贪吃蛇最大占满整个地图int size;//实际身体个数蛇头()和蛇身蛇尾(*)BODY food;//食物,#COORD coord;//(定位光标)的坐标,显示int dx;//移动方向int dy;//移动方向BODY tail;//记录蛇尾,用于清除蛇尾痕迹 int score;//分数BODY disability;
}SNAKE;//3.实现显示地图,初始化蛇函数,初始化食物函数void test_ui()
{for (int i 0; i H; i){for (int j 0; j W; j)printf();printf(\n);}
}
void disability_snake(BODY* disability)
{Sleep(5);//设置障碍物坐标disability-x rand() % W;disability-y rand() % H;while (disability-x 0 || disability-x W - 1 || disability-y 0 || disability-y H - 1){disability-x rand() % W;disability-y rand() % H;}}
void init_food(BODY* food)
{//设置随机数种子srand(time(NULL));//设置食物坐标food-x rand()% W;//[0,W-1],注意%的操作很巧妙food-y rand() % H;//[0,H-1]while(food-x 0 || food-x W - 1 || food-y 0 || food-y H - 1){food-x rand() % W;food-y rand() % H;}}
void init_snake(SNAKE* snake)//初始化
{snake-list[0].x W / 2;snake-list[0].y H / 2;snake-list[1].x W / 2 - 1;snake-list[1].y H / 2;//这里老师写成H/2-1应该是错的snake-size 2;//初始时蛇的长度为2,因为有头有尾//设置移动方向snake-dx 1;//默认移动方向向右snake-dy 0;//默认移动方向向左snake-score 0;init_food(snake-food);disability_snake(snake-disability);while (snake-food.x snake-disability.x snake-food.y snake-disability.y){init_food(snake-food);disability_snake(snake-disability);}
};void show_ui(SNAKE* snake)
{//显示蛇for (int i 0; i snake-size; i){ snake-coord.X snake-list[i].x;//这个函数是在显示地图后执行的,画完地图后光标是在地图之外的,用这个函数可以重置光标位置,让光标放到我们想要的地方snake-coord.Y snake-list[i].y;//定位光标位置,windows自带函数SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake-coord);if (i 0) {//显示蛇头printf();}else{//显示蛇身和蛇尾printf(*);}}//显示食物snake-coord.X snake-food.x;snake-coord.Y snake-food.y;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake-coord);printf(#);//显示障碍物snake-coord.X snake-disability.x;snake-coord.Y snake-disability.y;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake-coord);printf(?);//清除蛇尾的痕迹,否则一连串的*snake-coord.X snake-tail.x;snake-coord.Y snake-tail.y;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake-coord);printf( );
}
void show_wall()//展示墙
{for (int i 0; i H; i){for (int j 0; j W; j){if (i H || j W)printf();else printf( );}puts();}
}void hide_cur()//隐藏光标里面是windows特有的函数,不需要刻意去记拿来就用
{CONSOLE_CURSOR_INFO cci;cci.dwSize sizeof(cci);cci.bVisible FALSE;SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), cci);
}void move_snake(SNAKE* snake)
{ //记录蛇尾,清除痕迹snake-tail snake-list[snake-size - 1];//蛇身的移动,算法逻辑从后往前赋值//蛇头的移动,单独赋值for (int i snake-size - 1; i 0; i--){snake-list[i] snake-list[i - 1];}snake-list[0].x snake-dx;snake-list[0].y snake-dy;
}
void control_snake(SNAKE* snake)
{char key 0;while (_kbhit())//检测到按下按键{if (temp w _kbhit() s)key temp;else if (temp s _kbhit() w)key temp;else if (temp a _kbhit() d)key temp;else if (temp d _kbhit() a)key temp;else key _getch();}//循环之外,抬起按下动作,或没有按下动作//根据按键,修改移动方向temp key;switch (key){case w:snake-dy -1;snake-dx 0;break;case s:snake-dy 1;snake-dx 0;break;case a:snake-dy 0;snake-dx -1;break;case d:snake-dy 0;snake-dx 1;}}void snake_eat(SNAKE* snake)
{//判断是否吃到食物if (snake-list[0].x snake-food.x snake-list[0].y snake-food.y){//吃到食物,身体加长,食物消失,重新生成新的食物snake-size;//食物消失也不用重新写,因为一旦生成了食物原来的地方就消失了snake-coord.X snake-disability.x;snake-coord.Y snake-disability.y;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake-coord);printf( );init_food((snake-food)); disability_snake(snake-disability);printf( );while (snake-food.x snake-disability.x snake-food.y snake-disability.y){init_food(snake-food);disability_snake(snake-disability);}snake-score 10;}}
void game_over(SNAKE* snake)//游戏结束,指定到具体位置打印游戏结束
{snake-coord.X 40;snake-coord.Y 25;SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), snake-coord);printf(游戏结束 总分数:%d, snake-score);system(pause);
}
void snake_eat_body(SNAKE* snake)
{for (int i 1; i snake-size; i){if (snake-list[0].x snake-list[i].x snake-list[0].y snake-list[i].y){game_over(snake);//吃到身体;游戏结束exit(0);}}
}
void snake_push_disability(SNAKE* snake)
{if (snake-disability.x snake-list[0].x snake-disability.y snake-list[0].y){game_over(snake);exit(0);}
}
void start_game(SNAKE* snake)
{while (snake-list[0].x 0 snake-list[0].x W snake-list[0].y0 snake-list[0].y H)//在范围内移动{ //显示蛇和食物show_ui(snake);//控制移动方向control_snake(snake);//蛇吃到食物snake_eat(snake);//蛇吃到身体snake_eat_body(snake);//蛇撞到障碍物snake_push_disability(snake);//移动蛇move_snake(snake);//延迟300msSleep(100);}game_over(snake);//碰到墙壁,游戏结束
}int main(int argc,char *argv[])
{hide_cur();//隐藏光标SNAKE* snake (SNAKE*)malloc(sizeof(SNAKE));//创建空间init_snake(snake);//显示地图边界show_wall();//显示蛇和食物show_ui(snake);//启动游戏start_game(snake);free(snake);//释放空间//while (1);//先不让程序结束,就暂时不显示程序结束的那段字return 0;
}